Crux - A Programming Language for People
Chad Austin and I have been working on a programming language for the past 6 months or so. It is still not sufficiently stable that I’d recommend it for actual production use, but we’ve done enough that we think it might be interesting to language nerds.
Crux arose from a lot of research and personal experience on both our parts.
JavaScript
To start, we both have a lot of experience dealing with large, old code bases written in dynamic languages. We had the privilege of working with some tremendously smart, motivated people who all wanted to do the right thing, but we were nevertheless left feeling unsatisfied with the amount of work it takes to get good reliability and agility out of dynamic languages.
JavaScript is absolutely not the language we’d like to build our web applications in.
Haskell
Secondly, we’ve also got a lot of experience on the opposite extreme: we have both written quite a lot of production Haskell. We love the fidelity of Haskell’s type system and how it helps real humans write good software that can still change even when it is large and old, but we found the human factors to leave something to be desired:
- In order to do anything with any data type, you have to move your cursor to the top of the file and add an
import
statement. Larger modules require dozens of imports. We’ve seen over a hundred imports in a single source file. - Haskell is lazy. Because of this, a lot more is required of the compiler to get reasonable code, and even then, it’s easy for a well-meaning person to write code that allocates far more memory than expected. (a “space leak”)
- There is a JS backend for Haskell, but the code it generates is very large (960kb for Hello World!) and is almost impossible for a human being to understand.
Haskell is great (we’re using it to author the compiler!), but it’s far from perfect, and we can’t use it on the web anyway.
OCaml
There exists a spectacular JS backend for OCaml called js_of_ocaml. It generates fast, somewhat readable JS, and the OCaml language itself is remarkably well thought out.
The problem is that (and I must stress that I think this regrettable) OCaml will never become a popular mainstream language, and it has nothing to do with OCaml’s theoretical soundness.
OCaml is culturally tonedeaf:
- Arrays use the syntax
[| 1 ; 2 ; 3 ; 4 |]
. Linked lists use the syntax[1 ; 2 ; 3 ; 4]
. - Tuples use
,
and do not require parens. The expression[1 , 2]
is actually a list of 1 tuple. - OCaml has no overloading. Adding integers is done with the
+
operator; to add floats, the+.
operator must instead be used. - OCaml has objects, but the method access operator is
#
, not.
or->
egdocument#createElement "text"
- Mutable data is created with the
ref
function. This looks great. Assignment, however, uses:=
. Reading a ref cell requires using the!
operator, much like you use the*
operator in C to dereference a pointer. egx := !x + 1
Crucially, there are very good historical and technical reasons why all of these things are the way they are, but contemporary programmers don’t look at that. We see let a = [|1; 2; 3|];;
and we’re done. No further justification is necessary.
OCaml is a surprisingly adept language for the web, but it can never be more than a tiny niche.
TypeScript
Lastly, we looked at TypeScript.
TypeScript looks as though it is purpose-made to be a success among JavaScript developers.
Almost all of its syntax is instantly recognizable to people coming from JS, C#, or Java, and it has a stellar story for working with untyped JS: the JS compiler is designed from the start to do very little more than perform extra type checking. If you strip the types from TypeScript, you get JavaScript.
Unfortunately, preexisting JS doesn’t necessarily map to any kind of sane static type system, so TypeScript is intentionally unsound. By this I mean that it is possible to write a valid TypeScript program that incorrectly uses a value of one type as though it has some other (unrelated) type.
TypeScript also wound up repeating the Billion Dollar Mistake.
Now, it’s certainly the case that TypeScript is a killer solution if you specifically have a preexisting JS application that you need to improve incrementally, but I think unsoundness and pervasive nullability fatally compromise a system’s resilience to change.
TypeScript is what I want to move my aging JS codebase to, but it’s not where I want to start, if I have any choice.
Crux
From these, we arrive at Crux’s key pillars:
- Crux helps you write programs that are still easy to change when they are old and large
- Compiled Crux is small, fast, and has predictable performance
- Crux looks like contemporary programmers expect
I’ll go into more detail about what this means in upcoming posts.