Leaving Sweetness for two weeks on the Coast

Retiring carML and coastML at two weeks

Bottom Line, Up Front:

  1. I’m retiring carML; it was an interesting project, but I had made some choices along the way that limited forward momentum

  2. I’ve spent the last two weeks working on coastML, a new, small ML dialect, similar to Yeti with some lessons learnt from carML

  3. coastML is almost at feature parity with carML, and I have some interesting things planned for it

leaving sweetness and retiring carML

carML has been an interesting project over these last 6 years; I’ve learnt a lot about writing an ML dialect, and it’s been fairly interesting. Having said that, carML’s code has evolved a lot over the years, largely due to design changes I had. For example, originally I was thinking about supporting gradual typing or the like in carML, so complex types like ref and array didn’t always take a type parameter. I also switched from OCaml style type of othertype style declarations to Scala-style type[othertype]; this change meant there was a lot of support for two completely different types of declaration styles within one code base. Additionally, functions originally had very traditional type1 → type2 ⇒ type3 style declarations, before I realized I really liked a simple function[type1 type2 type3] style much better; it’s simpler, it’s compact, and it’s easy to tell what the intent is. Additionally, since carML doesn’t support currying, the arrow style actually could be confusing as to what is going on.

Sometimes we need to be mature and realize when a project has run it’s course, and it’s time to move on; this is one of those times for me personally. However, I really did enjoy some things about the language:

  1. The idea to cut down types from OCaml to Scala and more was great; this allowed for simple parsing & compact design, without changing much of the intent.

    1. Consider (int * int * int) vs tuple[int int int]; they both mean the same thing, but it’s very clear what the latter is

  2. Namespaces are great for ADTs: Result.Ok makes a lot of sense to me

  3. No operator precedence is pretty nice

    1. but let’s allow inline operators for those normal use cases people have

  4. Tools win out; when you’re working in your own language, you need tools and output over designs and neat hacks

  5. Using carML to write carML was great, and I want more of that

Relaxing on the coast

As the coastML README file mentions, I was working on some Multics ideas a few weeks ago, and wondering if I could make code that was nicer than Python’s if-elif-else chains, but without upgrading to Python3.10. I actually played around with generating Python from carML previously, and so didn’t want to dive into that. Instead, I poked around Coconut for a bit, and it was an intriguing answer: all valid Python is valid Coconut, which just extends the language with ML-isms.

If you know me, you know that I’ve spent a fair amount of time in my various language designs to generate human readable code from compilers. This means that the output should be digestible, editable, and honestly near indistinguishable from code a human might write. Will we employ tricks or otherwise make things efficient? Absolutely, but not at the expense of legibility. This is exactly where my issue with Coconut came from; when I compiled Coconut code, it definitely resulted in Python code…​ but not code I would want to personally use or edit. This is fine if you want to use a language as a compile target, so no judgement there! However, my intent is often that I want to write in something that is pleasant to me to write in, but deliver it to people who may want to use it without installing my tools.

So I ended up, as I often do, thinking about Yeti. Yeti is a tiny ML dialect for the JVM, which elides most syntax in favor of simple constructions and interaction with the host language. I started thinking "oh, if you just added a let to Yeti here, or a def there, it would be pretty neat…​" and then I paused for a moment. Why do we always want to add to a language? Why not subtract, making things as minimal as possible. So I looked at Yeti, and combined it with a few things from carML, and sketched out a simple language I initially just called "experimental langauge No. 30:"

foo = fn x y {  
    # functions are just named bindings
    # there's no other real need for
    # syntax
    case x 
        | 10 { print_endline "ok, x is 10" } 
        | 11 { print_endline "ok, x is 11" }
        | (x >= y) { print_endline "ok, x is >= y" } 
        | _ { print_endline "oh no, x is none of the above" } 
    esac 
}
foo 10 5; 
foo 20 5;

It’s small, easily understood, and relatively similar to Yeti, with some of the ideas I’ve learnt from carML baked in. I’m still working on the initial bootstrap compiler, but there’s enough there that you can access it from Python3 quite nicely:

import carpet
src='a = 10;\nb = 20;\nfoo = fn x y {\n    x + y\n};\nfoo a b;\n'
c = carpet.CarpetPython(src)
c.load()
c.generate()

relax on the coast, stay for a while

I’ll be working on adding lots of things to this, namely other langauge targets like C and Golang, as well as adding depth to the compiler itself. I’d also like to port libraries I’ve written in ReasonML, such as RoseJSON, and make the ecosystem as friendly as possible. So relax on the coast, stay for while