[HN Gopher] Tao: A statically-typed functional language
       ___________________________________________________________________
        
       Tao: A statically-typed functional language
        
       Author : memorable
       Score  : 214 points
       Date   : 2022-07-04 14:11 UTC (1 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | tm-guimaraes wrote:
       | The promises are great, but will it deliver? It's seems the
       | language has a really huge scope with lots of hard problems to
       | solve.
       | 
       | The thing I loved about golang was that it's just "good enough"
       | in lots of areas instead of being the best or perfect. That
       | allowed more time ti works on other parts of an ecosystem.
       | 
       | But since Tao is an hobby project, i just hope the author goes
       | nuts and enjoy working on all those things. It is indeed a cool
       | lang.
        
         | zesterer wrote:
         | I am indeed "going nuts"!
         | 
         | As mentioned in the README, I don't see Tao as a production-
         | quality language (at least, for the foreseeable future). I'll
         | leave that to the experts. Instead, I'm more interested in
         | exploring the limits of new language design ideas (effect
         | systems in particular). There are already a few interesting
         | things Tao does that I've not seen elsewhere.
        
           | landonxjames wrote:
           | Came across this post [0] by Graydon Hoare (original author
           | of Rust and member of the Swift team) in an HN comment the
           | other day. Lots of interesting discussion about future paths
           | for language design work, including some discussion of
           | Effects Systems.
           | 
           | Curious how Tao is pushing the limits on that front?
           | 
           | [0] https://graydon2.dreamwidth.org/253769.html
        
             | zesterer wrote:
             | > Curious how Tao is pushing the limits on that front?
             | 
             | I don't want to put emphasis on "pushing the limits"
             | because I'm still very new to language design and mostly
             | self-taught. There are bigger and better languages pushing
             | the envelope further than Tao!
             | 
             | That said, I've been experimenting with:
             | 
             | - Expressing totality and type inhabitance in the type
             | system
             | 
             | - Effect systems (including user-defined effects, effect
             | handling, parametric effects, lowering to monadic IO, etc.)
             | 
             | - Typeclass inference via typeclass variables (Tao can
             | handle several cases that are ambiguous even in Rust, such
             | as the following: https://github.com/zesterer/tao/blob/mast
             | er/examples/debug.t...)
        
               | skavi wrote:
               | would you mind explaining the linked code?
        
               | zesterer wrote:
               | The code is a bit of a gimmick, not really the sort of
               | thing you'd have in a real program. It's just there to
               | demonstrate that the compiler doesn't need explicitly
               | telling which typeclasses associated types correspond to.
               | That said:                   fn show_negative A : A ->
               | Str where             A < Neg,             A.Output <
               | Neg,             A.Output.Output < Neg,
               | A.Output.Output.Output < Neg,
               | A.Output.Output.Output.Output < Show,         =
               | a => (----a)->show
               | 
               | This is a function, called `show_negative`, that is
               | generic over a type, `A`. The function takes an instance
               | of `A` and returns a `Str` (string).
               | 
               | In terms of implementation, the function is quite simple:
               | it just negates the input value a bunch of time and then
               | `show`s it as a string using the `Show` typeclass
               | (equivalent to Haskell's `show`: https://hackage.haskell.
               | org/package/base-4.16.1.0/docs/Prelu...).
               | 
               | Arithmetic operations such as negation are defined using
               | typeclasses. Here's the definition of `Neg`:
               | class Neg =             => Output             => neg :
               | Self -> Self.Output
               | 
               | This means that types that can be negated must provide
               | two things: an output type produced when they're negated,
               | and a function that actually performs the negation. For
               | example, `Nat` (natural number) looks like:
               | member Nat of Neg =             => Output = Int
               | => neg = fn x => (implementation omitted because it's a
               | built-in intrinsic)
               | 
               | i.e: when you negate a `Nat`, you get an `Int`.
               | 
               | The `where` clauses on the `show_negative` just
               | constrains the output type of negating the `A` in such a
               | way that _its_ output type is also constrained, in a
               | chain, with the final type in the chain being
               | `show`-able.
        
       | LelouBil wrote:
       | A couple of months agow I had never tried functional programming
       | languages. Having learned Rust in the past year (and loving it )
       | made me want to try out Haskell since I always wondered about
       | functional languages and I likes Rust's strong type system.
       | 
       | I go a bit dissappointed with Haskell, but right now I'm reading
       | Tao's README and this looks like everything I've ever wanted !
       | I'm gonna try it out right now.
        
         | throwaway17_17 wrote:
         | Can you expand on what disappointed you about Haskell. I'm not
         | looking to convert you or anything, just curious.
        
       | okasaki wrote:
       | Sorry, what's that font in the github screenshots?
        
         | zesterer wrote:
         | Iosevka Fixed Slab Semibold
        
       | kortex wrote:
       | Nice work! Looks super interesting and rewarding to work on.
       | 
       | Is the namesake "tao" meaning "the way"? Or perchance a nod to
       | Terry Tao?
        
         | zesterer wrote:
         | > Tao or Dao is the natural order of the universe whose
         | character one's intuition must discern to realize the potential
         | for individual wisdom
         | 
         | It echoes how I feel when I write programs in languages with
         | strong static type systems: that of the compiler almost
         | fatalistically leading me towards a solution that naturally
         | fits the problem space. A metaphor I like to use is that of an
         | artist knocking away pieces of marble to reveal the statue that
         | was always there beneath.
         | 
         | And, yes, it's great fun to work on! Everybody should give
         | writing a compiler and designing a language a try!
        
       | pjmlp wrote:
       | I guess not to mix with Tao, next gen OS for Amiga, based on
       | bytecode as distribution format.
       | 
       | https://www.edn.com/amiga-reborn-via-tao-alliance/
        
       | lloydatkinson wrote:
       | Somewhat familiar with FP and enjoy some of its benefits in my
       | projects but I haven't really used a full blown FP language like
       | Haskell. What exactly is a type class? I haven't found an
       | explanation that makes a lot of sense to me. I think at one time
       | I believed them to be some kind of contract like an interface in
       | a OO language.
       | 
       | Can someone explain?
        
         | zesterer wrote:
         | A typeclass (or in Rust, a trait) is something that describes a
         | set of behaviours that must be implemented by a type (in OOP
         | languages, abstract classes are broadly equivalent).
         | 
         | For example, the typeclass `Eq` describes how values of a type
         | might be compared for equality.                   class Eq =
         | => eq : Self -> Self -> Bool
         | 
         | The typeclass requires that types conforming to it provide a
         | function, `eq`, that takes two instances of itself and returns
         | a bool (indicating whether the instances are equal). And member
         | of this typeclass might look like:                   for A, B
         | member (A, B) of Eq where             A < Eq,             B <
         | Eq,         =             => eq = fn (a0, b0), (b0, b1) => a0
         | == a1 && b0 == b1
         | 
         | You can read this as "for any two types, A and B, a tuple of
         | both can also be compared for equality provided both A and B
         | can themselves be compared for equality"
         | 
         | Typeclasses become useful because they allow you to constrain
         | generic type parameters. For example, we can now write a
         | function that determines whether a list contains a matching
         | value:                   fn contains A : A -> [A] -> Bool where
         | A < Eq,         =             | _, [] => False             \ k,
         | [x .. xs] => x == k || xs->contains(k)
         | 
         | All of this is checked at compile-time by the compiler,
         | ensuring that the function can only be called with types that
         | can be compared for equality.
        
         | lmm wrote:
         | One of the things that's hard to follow is that people conflate
         | typeclasses and typeclass instances.
         | 
         | So, the typeclass itself is like an interface, but rather than
         | having classes that implement that interface directly, you have
         | typeclass instances which are like an adapter between a value
         | (struct) and the interface - or like a virtual function table.
         | You know how a class instance is essentially a struct + a
         | (pointer to a) virtual function table? Imagine making those two
         | pieces more distinct in code - the typeclass instance is the
         | stanadlone virtual function table.
         | 
         | So your object state is more exposed - you can't really have
         | private fields in this paradigm since your state is just a
         | struct value. But it's clearer what's what, and it's much
         | easier to adapt types to interfaces even when the authors
         | didn't know about each other, since the typeclass instance can
         | be defined separately from both the typeclass and the value
         | type (although there are possible inference problems with
         | this). So lots of secondary cross-cutting functionality
         | problems where OO languages tend to end up with an ad-hoc type
         | registry (e.g. serialization or database mapping - just look at
         | what projects like jackson-datatype-joda or joda-time-hibernate
         | have to do to get themselves registered) are easier to solve in
         | this model.
        
       | galaxyLogic wrote:
       | What about OOP classes and inheritance? Data hiding? Modules?
        
         | zesterer wrote:
         | Tao has typeclasses (like Haskell) that allow for compositional
         | parameteric polymorphism. It also has data types (records, sum
         | types, etc.) and functions, but deliberately does not bundle
         | the two together.
         | 
         | There is no module system yet (only a temporary C-like #include
         | system): that's on my list of things to work on!
        
       | toastal wrote:
       | I don't understand what the possible use case for using a \ for
       | the final bar for sum types would be. It would just make it a
       | diff to add a new item at the end. I was hoping it had Unicode
       | support looking at the example screenshot, but no, it's just a
       | ligature font confusing users.
        
         | zesterer wrote:
         | Tao isn't indentation-sensitive, so nested `match` expressions
         | are ambiguous without the trailing \ branch.
         | 
         | I don't know whether I'll keep this syntax though. I'm
         | increasingly wondering whether it's better to just bite the
         | bullet and go all in with indentation sensitivity.
         | 
         | That said, the existing \ syntax can be quite nice to read:
         | https://github.com/zesterer/tao/blob/master/lib/parse.tao#L5...
        
           | ratww wrote:
           | The \ syntax is my favorite part so far, I'm definitely gonna
           | copy it for my toy language, hope that's ok ;)
        
             | zesterer wrote:
             | Go for it! IIRC it was suggested by someone else in the
             | #langdev channel on the Rust Community Discord, so I can't
             | claim to own the idea.
        
           | catlifeonmars wrote:
           | Why not just require parents for nested cases? IMO this is
           | fairly unambiguous, even to people unfamiliar with a
           | particular language.
        
             | zesterer wrote:
             | In a previous revision of the language, I did!
             | (https://github.com/zesterer/tao/tree/old). I decided
             | against it because, in short, I find the trailing
             | delimiters to be quite ugly.
             | 
             | Because this is purely a personal project, I'm thankfully
             | not constrained by such mundane concerns as "ergonomics"
             | (unless such concerns relate to me while I'm working with
             | it). I think Pythonic syntax is probably the way to go
             | long-term though...
        
               | patrickkidger wrote:
               | Personally I'm a big fan of Julia's syntax here.
               | Indentation-insensitive with the use of the word "end" as
               | the terminator for all types of block. In practice you
               | can lay it out like Python, and the "end"s help you keep
               | track when e.g. you need to place a single expression
               | after some deeply-nested blocks.
        
       | solatic wrote:
       | > Totality
       | 
       | Best of luck. Dhall is a current language with totality, but it
       | runs into impossibly high memory requirements as a result. We had
       | CI workers with 16 GB RAM run into out-of-memory issues because
       | of the sheer size of the totally expanded type space (sum types
       | of types which are themselves sum types of types which are
       | themselves sum types etc... Exponential growth is easy).
       | 
       | I appreciate that this is scoped as a hobby project for now, not
       | recommended for production, so like I said, best of luck :)
        
         | atennapel wrote:
         | I'm not sure what you mean with totally expanded type space.
         | But it sounds like Dhall has an issue with
         | unfolding/normalisation.
         | 
         | Totality shouldn't have any special impact on memory usage as
         | far as I know. It just restricts the kind of recursive
         | functions and data types you can write.
        
       | iamwil wrote:
       | What were the series of resources or readings that you did to
       | help you create Tao?
        
         | zesterer wrote:
         | There wasn't really any single resource I read. Frustratingly,
         | I find it quite difficult to read research papers (I'm told
         | it's a skill acquired with practice, but it's something I've
         | never gotten into). Most of the implementation is the product
         | of several rewrites and false-starts, along with the occasional
         | hint I've read about here or there on the web.
        
       | darthrupert wrote:
       | Hardly ever is "good REPL" one of the features of these new
       | languages. Do they think it's irrelevant or is making such a
       | thing too difficult compared to all these more CSy features?
        
         | zesterer wrote:
         | REPLs are often difficult to reconcile with AoT compilation and
         | static analysis, particularly in the context of things like
         | type inference (at least, in a way that preserves semantics).
         | It's on my mental todo list, but not a priority for me.
        
           | pohl wrote:
           | Do you suppose that might be an outmoded historical bias? I'm
           | wondering if maybe the advancement of hardware has made it so
           | that the costs of AOT compilation and a type system should no
           | longer stand in the way of delivering a reasonable REPL
           | experience. Swift's "playgrounds" seems to do alright -- a
           | real outlier in this regard.
        
             | skavi wrote:
             | Haskell has a REPL. No idea whether it's considered good
             | though.
        
           | catlifeonmars wrote:
           | How fast is the compiler? In many cases a report can just be
           | syntactic sugar for compiling and running an accumulating log
           | of source code lines. It's not really important how it works
           | under the hood as long as it's mostly transparent to the
           | user.
           | 
           | As a side effect, it might also be a good way to keep your
           | compile times down.
        
             | substation13 wrote:
             | What if one of those lines fires the missiles?
        
             | zesterer wrote:
             | Fast enough for this approach to work for small examples.
             | For longer examples, I'm less confident: I've yet to
             | implement a proper module system (other than a slightly
             | more principled version of C's #include) so this currently
             | means compiling the entire standard library on every REPL
             | line. Thankfully, the compiler can still handle this within
             | about 100 milliseconds on my machine. Perhaps when I have a
             | more rugged module system I can look into this more, thanks
             | for the suggestion!
        
             | darthrupert wrote:
             | That's not even close to being adequate for the kinds of
             | things good REPLs are good for.
        
       | shirogane86x wrote:
       | This looks great! I've actually been experimenting with making a
       | functional language too, in rust - so I'm probably gonna read
       | through the code soon! Also, those error messages look really
       | good - I'm totally gonna try ariadne and chumsky out :)
        
         | zesterer wrote:
         | Good luck! Feel free to open a discussion topic or issue if you
         | have any questions!
        
           | throwaway17_17 wrote:
           | Since someone brought it up, and since I don't speak rust...
           | are the error message formats (i.e. the lines and crossings)
           | made by a rust library or did you implement them? They are
           | certainly visually nice.
        
             | zesterer wrote:
             | They are made by a Rust library, but it's a library I wrote
             | for this project (now used by others too):
             | https://github.com/zesterer/ariadne
        
       | magicalhippo wrote:
       | Great readme, and definitely made me want to play with it. I was
       | raised on procedural programming and never really got into proper
       | functional programming, but somehow this just clicked for me.
        
       | erezsh wrote:
       | Asking as a complete newbie in this paradigm, but what's the
       | benefit of writing factorial like this?                   fn
       | factorial =              | 0 => 1             \ y ~ x + 1 => y *
       | factorial(x)
       | 
       | Instead of                       | y => y * factorial(y-1)?
        
         | zesterer wrote:
         | Subtraction of natural numbers is non-total, and can result in
         | negative numbers, which cannot be represented as a natural
         | number. Most languages throw an exception, panic, etc. None of
         | these are solutions I'm happy with.
         | 
         | In Tao, natural numbers don't support subtraction but you can
         | decrement them by pattern-matching as in this example, allowing
         | the compiler to 'check your workings' and statically prove that
         | your program can never result in underflows.
         | 
         | In a similar vein, you can't index directly into a list
         | (because indexing is a fallible operation). Instead, you must
         | pattern-match on the list and explicitly handle the empty case
         | (you can also write a utility function to do this for you like
         | so)                   fn nth : Nat -> [A] -> Maybe A =
         | | _, [] => None             | 0, [x ..] => Just x             \
         | n + 1, [.. tail] => tail->nth(n)
         | 
         | Used like:                   [1, 2, 3]->nth(1) # Evaluates to
         | `Some 2`
        
       | Akronymus wrote:
       | A few observations:
       | 
       | Seems very ML/Haskell inspired.
       | 
       | All tuples seem to require parenthesis.
       | 
       | Definitely not ergonomic on a qwertz layout (Altough, what
       | language is?)
       | 
       | No current plans for an interactive/repl version it seems
       | 
       | No dependant types either.
       | 
       | Overall, pretty interesting. Definitely warrants a closer look.
        
         | zesterer wrote:
         | Pretty much!
         | 
         | The language does support limited sugar for tuples without
         | parentheses in some specific cases, such as `match` and `let`:
         | let x, y = 5, 4 in         ...              match x, y in
         | | 4, 7 => None             \ _, y => Just y
         | 
         | However, these cases only incidentally lower to tuple pattern
         | matching, and deliberately hide the fact that tuples are used
         | internally.
        
           | Akronymus wrote:
           | Are you planning on having something similar to computation
           | expressions? Altough, I guess thats basically covered within
           | the do notation.
           | 
           | https://docs.microsoft.com/en-us/dotnet/fsharp/language-
           | refe...
        
             | zesterer wrote:
             | Yes! As you say, the do notation + monads covers this, but
             | I'm planning to remove it in favour of a slightly more
             | general 'effect basin' syntax that I'm currently working on
             | (syntactically similar to Rust's async + await, but
             | generalised to all effectful operations like mutation, IO,
             | etc.). Example here: https://github.com/zesterer/tao/blob/m
             | aster/examples/mutate....
        
         | platz wrote:
         | > No dependant types either.
         | 
         | You're funny
        
           | Akronymus wrote:
           | How so? I personally find dependant types to be very
           | interesting, but I get that they are a PITA to implement. So,
           | for me, it was simply an observation that there are currently
           | no plans for dependant types. No value judgement intended.
        
             | isaacimagine wrote:
             | Not to be pendantic, but I think you made a joke without
             | realizing it.
        
               | Akronymus wrote:
               | I did? I have now spent like 20 minutes trying to figure
               | it out. But I still don't get it.
        
               | macintux wrote:
               | I'm guessing it has something to do with the keyword
               | "either" and Haskell types, but I'm otherwise clueless.
               | Every time I look at Haskell I run the other way.
        
               | zesterer wrote:
               | I think it was "value judgement" (dependent types allow
               | dispatching over a value in the type system)
        
       | maweki wrote:
       | So is the language planned to be computationally complete or
       | total?
       | 
       | I was not completely clear on your definition of totality. But I
       | am all for not Turing-complete models of computation that still
       | do useful things.
        
         | zesterer wrote:
         | For now, just total (i.e: functions can't panic, throw
         | exceptions, etc.). I'd like to explore termination analysis in
         | the future though!
        
           | samatman wrote:
           | It's an unfortunate conflation between total functions and
           | _total functional programming_ , which is done with non-
           | terminating languages. If I'm reading you right, Tao
           | currently requires functions to be total but is not designed
           | to always terminate.
        
             | zesterer wrote:
             | Yes. This is something I've been thinking about a lot, as
             | it happens! In particular, one of the great benefits of
             | pure languages from an optimisation standpoint is that
             | unused values can always be optimised away, but this is not
             | the case if we treat termination (or non-termination) as a
             | side effect! For now, I'm treating it as not being a side-
             | effect (as does C++, although C++ goes a step further and
             | treats it as straight-up UB in certain conditions). This is
             | something I've still yet to nail down semantically.
        
       | zesterer wrote:
       | Hey, author here. I definitely didn't expect to see Tao on Hacker
       | News. As is probably obvious, the language is _extremely_ early
       | in its life, and it 's not practical to write anything but
       | trivial examples in it yet. Please don't judge!
        
         | agumonkey wrote:
         | good luck
        
         | code_biologist wrote:
         | I remember a golden age of programming reddit and HN in the
         | late 2000s that inspired me learn a bunch of new programming
         | languages, Haskell the biggest influence among them. It seemed
         | like people were always posting their own personal languages,
         | lisps (I remember the hot takes on Arc lol), and such all the
         | time. I have very fond memories of going through Jack
         | Crenshaw's "Let's Build a Compiler" after seeing it posted a
         | bunch of times, then trying to build my own language in a
         | similar vein.
         | 
         | Cheers to you, and I hope you're learning and having fun
         | working on your project!
        
           | zesterer wrote:
           | Thanks!
        
             | jrumbut wrote:
             | I too remember that time and it left me terribly jaded
             | about the utility of new languages.
             | 
             | This is the first I've seen in a while where I think the
             | offering hits a spot on the tradeoff continuum that is
             | distinctive and where I can picture many circumstances
             | where I would want to use this language (the soundness
             | aspect is a big part of that).
        
         | dmix wrote:
         | Nice work on the README. Gets right to the point about what it
         | look likes and what the goals are.
        
         | yccs27 wrote:
         | Tao looks super cool, and similar to the kind of language I'd
         | personally make if I decided to create one. Kudos for actually
         | implementing yours!
         | 
         | Some specific questions about the language design: - Does your
         | flavor of algebraic effects allow distinct effects of the same
         | type (e.g. two separate int-valued `State`s)? I haven't seen
         | anyone talk about this, but it seems like a potential problem
         | with effects.
         | 
         | - Do you have plans for making pattern matching extensible?
         | I've thought a lot about how this could be possible with optics
         | (the FP construction), but haven't found a nice solution yet.
        
           | zesterer wrote:
           | > Does your flavor of algebraic effects allow distinct
           | effects of the same type
           | 
           | Could you give an example of what you mean by this? Multiple
           | effects can be combined together (although I'm still working
           | on handlers for multiple effects), for example:
           | effect console = input + print              fn greet_user :
           | console ~ () = {             print("Please enter your
           | name:")!;             let name = input!;
           | print("Hello, " ++ name)!;         }
           | 
           | > Do you have plans for making pattern matching extensible?
           | 
           | I have no plans as of yet. Extensible pattern-matching seems
           | to me to be quite hard to unify with exhaustivity. Tao
           | supports most of the patterns that Rust/Haskell supports.
        
             | throwaway17_17 wrote:
             | I think GP is referring to having two effects, like State1
             | and State2, both of which allow for stateful effects on int
             | references, and then being able to handle the effects
             | uniquely for each effect. I think the underlying type
             | theoretical question would be are Tao's effects nominally
             | vs. structurally typed.
             | 
             | A more practical example would be having two state-like
             | effects for use as different allocation/deallocation
             | strategies.
        
               | zesterer wrote:
               | Tao's effects are nominally typed, so there's no way to
               | accidentally mix them up in the way I assume you're
               | describing. For example:                   # Define a new
               | effect that yields to the caller         effect yield A =
               | A => ()              # A generator that emits numbers
               | def one_two_three : yield Nat ~ () = {
               | yield(1)!;             yield(2)!;             yield(3)!;
               | }              # Print the numbers to the console
               | def main : io ~ () = {             one_two_three
               | handle yield Nat with n => print(n->show)!         }
        
         | roguas wrote:
         | Designing a PL is a hard complicated process, very slim portion
         | of professional programmers are able to deliver some novelty
         | here. I think any judgement should be treated as merely a
         | feedback on the usefulness of this language. The effort alone
         | is exceptional accomplishment. Take care.
        
           | zesterer wrote:
           | Thanks for your kind words :)
        
         | dataangel wrote:
         | > A rather dogged and obnoxious opinion of mine is that the
         | 'optimisation ceiling' for statically-typed, total functional
         | programming languages is significantly higher than traditional
         | imperative languages with comparably weak type systems.
         | 
         | Curious about plans for this. When Haskell use exploded I
         | followed a lot of the blogs talking about it someday being
         | faster than C because purity made compiler reasoning easier and
         | stream fusion and other things were going to change the world,
         | but as time went on idiomatic Haskell never came even close to
         | C. Now having a lot of performance experience I think a major
         | factor is that functional languages insist on using lists for
         | everything, which map poorly to hardware. Looking at your
         | screenshots, it looks like it still employs the list paradigm.
        
           | zesterer wrote:
           | So... I want to preface this by saying that I'm very, very
           | far from being an expert on this topic. Although Tao's
           | compiler has a MIR optimiser, it only covers the basics
           | (inlining, constant folding + symbolic execution, etc.).
           | 
           | I think one of the main reasons that Haskell failed to solve
           | this is actually the same reason that many lower level
           | languages failed: it places too many requirements on data
           | representation. In the case of C/C++ and even (to a lesser
           | extent) modern languages like Rust and Swift, this is because
           | they make promises about representation to the programmer
           | that allow you to circumvent aspects of the language and
           | still write correct code: be it transmutation, casting, field
           | offsets, etc. In the case of Haskell, lists have an entirely
           | arbitrary length and the language makes no effort to
           | constrain this requirement in the type system, meaning that
           | the compiler can only speculatively optimise a list into an
           | unboxed array. In a language with dependent types, it should
           | be possible to constrain the size of a list with the type
           | system, allowing the optimiser to do its job without need for
           | speculative whole-program analysis.
           | 
           | The other reason Haskell doesn't quite succeed is
           | monomorphisation (or lack thereof). Haskell's support for
           | first-class higher-ranked types means that the language can't
           | feasibly make promises about monomorphisation, and as a
           | result it needs to revert to boxing and dynamic dispatch far
           | more than it really should. Conversely, Tao is designed to
           | monomorphise in all cases from the start.
           | 
           | Rust demonstrates that functional programming (and in
           | particular, programming with higher-order functions) is more
           | than possible to optimise very well, and it does this by
           | promising monomorphisation through the type system, allowing
           | the compiler to aggressively perform static dispatch and
           | inlining.
           | 
           | From what I've seen, GHC also fails to pick a lot of low-
           | hanging fruit. The last time I checked (perhaps this has
           | since changed) GHC often struggles with things like TCO and
           | inlining in relatively simple cases as a byproduct of its
           | design (all functions are dynamically dispatched by default,
           | with inlining being a speculative optimisation).
           | 
           | I need to do a little more writing about exactly what ideas I
           | have for Tao, but other languages of similar ilk demonstrate
           | that Haskell is very far from the pinnacle of what is
           | possible (for example, Koka's Perceus reference reuse:
           | https://koka-lang.github.io/koka/doc/book.html#why-perceus).
        
             | shadowofneptune wrote:
             | > all functions are dynamically dispatched by default, with
             | inlining being a speculative optimisation
             | 
             | Could you please point me to other approaches to inlining?
             | I am also working on a personal language and it could be
             | helpful.
        
               | zesterer wrote:
               | Rust gives each closure a unique type (see the `Fn`
               | trait: https://doc.rust-lang.org/core/ops/trait.Fn.html),
               | allowing the compiler to always statically dispatch to
               | the exact function body at compile-time. That said, this
               | is a rather more explicit approach that won't necessarily
               | fit everything.
        
         | slavapestov wrote:
         | I'm curious how you implement associated types and equivalences
         | between them. In the most general case, this allows encoding an
         | arbitrary finitely-presented monoid in the type system, and the
         | word problem on finitely-presented monoids is undecidable.
         | However, you mention your type checker is Turing-complete.
        
           | zesterer wrote:
           | Yep, this is the case: the type-checker allows arbitrary
           | computation to occur at compilation time. You can see some of
           | that here, where I've implemented peano
           | arithmetic/addition/multiplication/branching using the type
           | system:
           | https://github.com/zesterer/tao/blob/master/examples/type-
           | as... . I've not yet had the time or mental fortitude to
           | implement something more non-trivial like N-queens or a
           | Brainfuck interpreter, but perhaps eventually!
        
       | slewis wrote:
       | Very cool! This might be a naive question. Are your arithmetic
       | patterns equivalent to dependent types, like those found in
       | Idris?
        
         | zesterer wrote:
         | They're more akin to 'destructuring' natural numbers into
         | successors. Consider:
         | 
         | data Nat = Zero | Succ Nat
         | 
         | You could pattern match on this Nat, fetching the inner Nat,
         | allowing the type system to prove that all cases are handled
         | exhaustively. Arithmetic patterns just extend this notion to
         | the built-in Nat type.
         | 
         | Full dependent typing is something I'd like to experiment with
         | in time, but I'm not there yet.
        
       | schaefer wrote:
       | The Tao that can be told is not the true Tao.
        
       ___________________________________________________________________
       (page generated 2022-07-05 23:01 UTC)