Take

Weave: part one

Ever since I read Nikita Prokopov's Software disenchantment, I've been going down the rabbit hole. The article is about software quality going to crap, and how often it is linked with expediency.

It contains a mention of Jonathan Blow's language Jai, which made me look it up, and whose history and philosophy I gradually soaked up after watching a number of his archived streams on YouTube which are largely either progress reports or him making a change live. (The name, as far as I can tell, is never explained nor even said on camera, which is a rare feat given the screen time.)

The gist of this philosophy is basically: "speaking as a game developer with Braid and The Witness to my name, to whom details are important both in that they make my game what it is and in that they dictate whether given the previous details I will even be allowed to hit 60 fps, I am thoroughly disappointed with today's programming landscape, most languages and most current dogma on architecture and abstraction, so therefore I'm making my own programming language from scratch so I can make it do the things I think it should be able to do".

Jai is enthralling because of these perspectives. It has metaprogramming-that-is-also-compile-time-code-generation-which-feeds-into-runtime-introspection on a level that is rare for how close the language gets to the metal. Generics are turned into "polymorphic functions" which are fully mapped out and realized at compile time from actual use. Each top-level declaration or function is parsed and type-checked separately and fed into a "meta-program" using which you can choose to produce new code or alter the just-parsed code. There's no such thing as classes, and indeed no constructors or methods - but basic levels of "inheritance" are possible through struct containment and the keyword using, which turns into syntactic sugar for going up the chain if you can't find it, but by the compiler doing lookups instead of via virtual method tables.

The reason for all of these things are that they allow flexibility in the code while still not prescribing implementation. You're supposed to make your own of most things, because in Jonathan's world that's basically how it is anyway – the memory layout of the list of entities affects performance, so why wouldn't you roll your own, or at least want to be able to? Earlier versions contained support for managing turning an "array of structs" into a "struct of arrays" — ie being able to pack the base metadata of all entities in a denser form, and so on – while still using the same syntax.

Having always carried the stereotype that people who cared so much about performance never aspired to neater code, or at least never let it bother them that they couldn't have it, I was disturbed at first by all this. Heaven knows how rudimentary code completion or "go-to-definition" type analysis would be implemented for this language, and that's probably why this road isn't often taken. Security experts would run from numerous aspects of the language and its model, and it doesn't attempt to make things easier for people who screw up memory safety in a pointer-oriented language (which at last count is still literally everyone).

There are numerous reasons to "not try this at home" and Jai is explicitly not designed for general use by everyone, but those concerns are not reasons to avoid looking at an unconventional language that throws off, at least temporarily, the yokes of soundness and theory and abstraction in exchange for actual utility and usefulness – and even other kinds of abstraction.

Previous post: Optimizations and tweaks Following post: Weave: part two