Updates on 2021/12/24

Noteworthy features of Starlark

Starklark is Bazel's configuration language, and not designed to be general-purpose. Nontheless, there are some features that seem useful even for a dynamic G/P language.

  1. Single final assignment at the top level. Module-global functions, variables cannot be re-bound. This helps reading code, and simplifies tooling.
  2. Deterministic iteration order for dictionaries, and in general determinism (a program run twice always produces the same outcome, modulo things like time). Determinism seems like a generally desirable property, for things like testing/reproducible builds.
  3. No mutation of iterator during iteration. Mutating the iterator (like a list) during iteration panics the program, to avoid iterator invalidation errors.
  4. No [checked] exceptions. Panicking the program for any non-anticipated error might seem problematic, but "makes the language simpler and reduces the number of concepts." Exceptions also become API surface for the language, so not having it helps language evolution.
  5. Strings are not iterable. This avoids bugs from passing in a string instead of a length-1 list of strings to APIs expecting a list.

Starlark, a configuration language for the Bazel build tool, has a tree-walking interpreter implemented in pure Go at google/starlark-go.

It seems like the canonical implementation of Starlark is the Java implementation in the Bazel source tree, but the Go version is used "in production" in web playgrounds, debuggers, and so on.

starlark-go is notable because it's one of a vanishingly small number of production language implementations that are tree-walking interpreters rather than bytecode VMs. The implementation guide says:

The evaluator uses a simple recursive tree walk, returning a value or an error for each expression. We have experimented with just-in-time compilation of syntax trees to bytecode, but two limitations in the current Go compiler prevent this strategy from outperforming the tree-walking evaluator.

The details of why exactly that's the case is interesting, and documented further in the link, but seem inherent to Go's current compiler design and philosophy. It also supports Oak's current (tree-walking) evaluator design, which is nice.

Typing on a typewriter for a while and then going back to a shallow laptop keyboard is a trippy experience.