(↑)

Languages

I keep writing new programming languages. Not sure why.

It was probably inevitable. After too many years of writing code in programming languages, I gave in to the voices in my head that kept asking "why do we have to do it like this? Isn't there something better?"

Throff

Throff is my Meisterwerk. Before this, I felt like a tourist in the world of programming, now I feel like I have a good grasp of how programming languages work.

Throff is a dynamically typed, late binding, homoiconic, concatenative programming language. It was an attempt to take Forth and update it for the modern world of programming, by building the entire system up from a small foundation of functions, and a tiny simple interpreter.

It works completely, but the result is just another modern programming language, which fits in the same niche as Perl, Python, Ruby, PHP. An interpreted language that is pleasant enough to write in, has the flexibility and power to redefine its own syntax and extend it in convenient ways.

There are now several implementations:

The combined source lives in github.com/donomii/throff-combined and includes a VM spec, a library of examples, and the cross-platform downloads linked from the Throff project page.

It has some interesting internals, as it uses a three-stack model, with the third stack being the environment, which provides continuations and useful scoping tricks. The imlementation allows for things like perfect tail calls, allowing complex control structures to operate without needing to consider stack management.

Linc

Linc (in ~/git/racketprogs/linc) is my attempt to make a useful systems level language using some very advanced concepts like linear typing and ADTs. In benchmarks, it lands somewhere around the Rust level, which is about right.

The final language is starting to look similar to C, but with more type declarations, and some very different (better) behaviour.

The compiler is written in C and emits C, which gets handed to a target C compiler. A --static flag asks for a single-binary native build. There's a std/ library covering math, files, paths, env, config, JSON, threads, HTTP, AWS SigV4, DynamoDB, ClickHouse — the kind of plumbing you actually need to write a useful program. The driver only links in support libraries (libcurl, OpenSSL, OpenGL, GLUT, etc.) for features the program actually uses, so simple programs stay small.

Linc is my attempt to find out whether a linear, concatenative language can be pleasant to write real software in. So far: surprisingly yes.

xsh

xsh (in ~/git/racketprogs/xsh) is a cross-platform shell and scripting language.

xsh ships as two cooperating binaries: xsh is the interpreter, and xshguardian is a sibling process the shell delegates external-program execution to, so signal handling and PTY allocation don't block the parent interpreter. There's an embedded standard library (stdlib.xsh) with map, fold, reverse, range, boolean combinators built on nand, a default prompt, and a small lined line editor with history, completions, F-keys, and Ctrl-R search.

The language has a spec and a pseudocode walkthrough of the implementation.

I want my shell to be a real programming language, and I want my programming language to be a real shell. xsh is that experiment.

Quon (and the Quonverter)

Quonverter (in ~/git/racketprogs/quonverter) is an attempt to make the "unviersal programming language", something that runs everywhere, on every machine. A language that can run on every platform, works with every compiler, and can adapt for new environments.

It turns out, this is possible, so long as you don't try to actually do anything useful with it.

At the moment, it is a transpiler that takes a small S-expression-flavoured language — quon — and emits source code in C, Perl, Node.js, Java, or Lua.

The language is deliberately the lowest common denominator across those targets — functions, structs, conditionals, recursion. No closures, no coroutines, no Hindley-Milner. The whole point is that anything written in quon is automatically available as a library in any of those host languages. Write once, get five language ports for free.

The compiler is self-hosting: compiler.qon compiles itself. A bootstrap/ directory keeps pre-built versions of the compiler in each target (quon.exe, quon.pl, quon.js, quon.lua, quon.java), so I can rebuild from any of them. There are circular-build scripts (circular.bash, circularperl.bash) that re-host the compiler in each language and verify it still works.

There is absolutely no reason for this language to exist, and nothing useful to do with it. But I think it's probably the best thing I have ever written.

Stalk

Stalk (in ~/git/racketprogs/stalk) is a small prototype-based language inspired by Smalltalk.

Everything is a message send: methods, control flow, even object construction. A statement ends with a period; a message send is receiver name: arg name: arg. and the selector is built from the argument labels — so a familiar Smalltalk idiom like y ifTrue: [| io print: "Yes\n"] ifFalse: [| io print: "No\n"] works as you'd expect.

io print: "Hello World\n".
io println: (string join: (list 1 "a" "c")).

y = "abcde" contains: "a".
y ifTrue: [| io print: "Yes\n"] ifFalse: [| io print: "No\n"].

Stalk is what PerlTalk wanted to be, after I figured out what had gone wrong with PerlTalk.

ServiceTalk

ServiceTalk (in ~/git/racketprogs/agentic/servicetalk) is a distributed Smalltalk where every object is its own OS process, running its own HTTP server, and every message send is an HTTP RPC (fire-and-forget POST plus callback). A central coordinator handles the registry, port allocation, spawning, and the shared transcript output.

./smt coordinator          # starts everything; spawns Main by default
./smt coordinator --main "" # coordinator only, no auto-spawn

ServiceTalk classes can be written in three shapes — the runtime tries each in turn:

Shape Source Language
Dog.st Traditional Smalltalk ServiceTalk
Dog.smt S-expression (class …) ServiceTalk
Dog/ Directory, manifest + C files C (foreign)

So a familiar Smalltalk class definition just works:

class Dog [
    | name breed |

    name: n breed: b [
        name := n.
        breed := b
    ]

    bark [
        Transcript show: name , ' says: Woof!'
    ]

    name [ ^ name ]
]

…and the same class can equivalently be written as an (class …) S-expression, or as a directory of C files with a manifest if you want a foreign-class implementation that still presents as an ordinary Smalltalk object to the rest of the system. Inheritance is single-parent (class Cat extends Animal [ … ]). There are bundled foreign classes for ClickHouse, DynamoDB, S3, a Robocop-style proxy, RCounter/CCounter, and an HTTP client/server pair.

The interesting part isn't the syntax — Smalltalk is Smalltalk — it's that an object reference is literally a network endpoint, and "send a message" is literally an HTTP request. A program written in ServiceTalk is a small fleet of processes that you can scale, redeploy, or rewrite individually. There's a Docker Compose file and a Kubernetes manifest in the repo for running real fleets. A Rust port (servicetalk-rs) lives alongside the Go implementation.

ServiceTalk is what happens if you take Smalltalk's "everything is an object, everything is a message send" seriously enough to make it true at the OS level.

Atto

Atto (in ~/git/racketprogs/atto) is a minimal functional language in Go. It's a re-implementation of zesterer/atto.

I really admire the way it's possible to make a complete language in 500-1000 lines of host code.

fn factorial n is
    if = 0 n
        1
        * n factorial - n 1

fn main is
    print factorial 5

A program is a sequence of fn name args is body definitions. Bodies are expressions: function calls, if, integer/string/boolean/list literals, and references to bound names. Lists are cons-cell pairs. Nothing else.

It's not quite compatible with the original.

PerlTalk

PerlTalk (in ~/git/racketprogs/PerlTalk) is a failed Smalltalk-interpreter-in-Perl experiment, plus several side projects in parser construction (BNF / PEG / ANTLR / brag).

The parser works — it can read a Smalltalk-flavoured program — but execution is incomplete. Nonetheless, it kicked off my interest in making programming languages, and informed several of my later attempts.

The interesting stuff that came out of it:

Smaller language-shaped things

These aren't full languages but they live in the same headspace: