1 unstable release

0.1.1 Oct 29, 2023

#545 in Programming languages

34 downloads per month
Used in vic

MIT license

125KB
3K SLoC

vicuna

Vicuna is a language that lets you write elegant, type-safe code that compiles to JavaScript. It should feel like a tasteful blend of JavaScript, Rust, and OCaml.

Status

This is a total work in progress. Anything written on here should be interpreted as a future aspiration and not as the current reality for Vicuna.

The name isn't even set in stone. I just liked the sound and wanted a name that referenced camels.

Currently the compiler can build and compile simple programs with basic type checking. The current goal is to add:

  • Struct and enum generics
  • Function generics with type inference
  • Traits
  • Enums and pattern matching (well, an MVP)

Why

Why am I writing Vicuna? I've enjoyed using languages that compile to JavaScript like TypeScript and ReScript (formerly ReasonML). Both offer such a compelling set of features like excellent JavaScript compatibility (TS) and type inference (RS). However, I've felt that each language needed a little of the other language. If ReScript only had good async support and some more editor compatibility, or if TypeScript only had sum types and integer types, then I'd be satisfied.

I also like Rust a lot and would love to see a language that manages to keep that pragmatic use of immutability and imperative programming even while not compromising on safety. Also, traits are excellent and make the developer UX nicer.

Finally, another goal is to make JavaScript: The Good Parts, the language. Not in the literal sense, but more in the vein of imagining what a TC39 unencumbered by backwards compatibility could accomplish. What if we could get do expressions and runtime types? What if we could change JavaScript's implicit casts? Of course, I do not speak for TC39 or any delegate to TC39.

Why Compile to JavaScript?

I believe that compiling to JavaScript is a severely underrated strategy. A lot of people are looking to WebAssembly as the standard compilation target for the web. I don't disagree for certain use-cases like intense, native-like computation. I've written my own attempt at compiling to WebAssembly, Saber. However, WebAssembly is still a very...constrained compilation target. With Saber I wrote and rewrote multiple compilation passes, with lots of static analysis and fancy symbol tables; I added my own runtime with garbage collection; I spend a lot of time thinking about memory layout.

You see, the difficulty in compiling Saber is that Saber, the source language, is very different from WebAssembly, the target language. Saber has all these high level concepts like closures, garbage collection, strings, while WebAssembly has low level concepts like linear memory, indices, and load/stores. Getting a compiler to translate from one to the other is rather challenging. But you know what does have high level concepts like closures, garbage collection and strings? JavaScript! By compiling to JavaScript I'm saving myself so much pain. And I didn't even get to the nasty stuff around interop, calling Web APIs, or debugging.

Granted, I was and am still very new to compilers. I'm sure an experienced compiler engineer could knock out something like Saber with ease. Heck I have a few friends who could do it. But for my skills, compiling to JavaScript is the best plan.

More grandly, I'd agree with the aphorism to "always bet on JavaScript". JavaScript may be maligned, but it's ubiquitous, and it works pretty darn well. It's a rare high level, dynamic language that performs decently. It has some really great libraries (and some...less great ones). As optimistic as I am about WebAssembly, I don't anticipate that we'll move away from JavaScript anytime soon.

Philosophy and Goals

The goal is to create a language that is as close to my goals of expression oriented, type safe, and ergonomic, while also being a relatively simple transformation to JavaScript. This does mean that there are some restrictions that need to be put in place. For instance, a if expression with blocks, while very nice, is not a simple transformation to JavaScript. However, a let binding that assigns to an if expression with blocks? That's a very easy transformation. Similarly a function that ends on an if expression is an easy transformation.

Dependencies

~5.5–7.5MB
~143K SLoC