joetjen.net
EN DE
Programming language

Praxis

Praxis is a Clojure-shaped Lisp that compiles to the BEAM and to JavaScript. Its compiler, runtime, standard library and toolchain are written in Praxis and compile themselves. It is in active development and not published yet.

In development · v0.63

What it is

Praxis takes Clojure’s syntax — literals for vectors, maps and sets, sequence functions, threading — and puts it on Erlang/OTP’s semantics: immutable bindings, multi-clause functions with guards, pattern matching everywhere, processes, supervision and hot code reload. It is a Lisp-1 and homoiconic; macros are written with syntax-quote.

On the BEAM every namespace becomes an ordinary .beam module, so Erlang and Elixir can call it like any other. On JavaScript every namespace becomes an ES module for node or the browser. Interop is direct: a Praxis record is an Elixir struct, a Praxis protocol is an Elixir protocol.

A first taste

demo.prax
(ns demo "A few functions.")

(defn split "Divides `total` among `people`."
  [total people] :spec [integer integer -> number]
  (/ total people))

(defn classify
  ([n] :when (< n 0) :negative)
  ([0] :zero)
  ([_n] :positive))

(split 4260 3)                  ; => 1420
(split 4250 3)                  ; => 4250/3
(seq/map [-5 0 7] classify)     ; => (:negative :zero :positive)
(-> "  night market " string/trim string/upper-case)
                                ; => "NIGHT MARKET"

Division is exact — 4250/3 is a ratio, not a rounded float. :spec declares the signature, which feeds Dialyzer, TypeScript declarations and property tests. The standard library takes its subject first, so -> is the pipeline.

Design

  • BEAM conformity first: where another BEAM tool could tell the difference, Praxis does what the BEAM does.
  • Few special forms. cond is the one primitive branch; if, when, and, or, case and with are macros over it.
  • A name keeps its value. Rebinding is a compile error, as in Erlang.
  • Numbers never overflow and division is exact: bignums, ratios and decimals are part of the language.
  • One source, two machines. Portable code runs unchanged on the BEAM, on node and in the browser — processes included.
  • Tools talk to people and to machines. Every prx command can answer in JSON, and an MCP server comes with the toolchain.

The name is Greek: πρᾶξις, doing — action that carries its end in itself, as distinct from knowing (theoria) and making (poiesis). Praxis is the compiled sibling of Logos, the embeddable Lisp; the two share a specified grammar but no code.

The toolchain

ToolDoes
praxisCompiles files and runs scripts; praxis explain PRX0208 explains a diagnostic
prxThe project tool: manifest, lock file, dependencies from Hex, git or paths, tasks, releases, plugins
preplThe interactive prompt, with live redefinition and :h for documentation
pluginsprx test, fmt, lint, check (Dialyzer, reported in Praxis terms), doc, docset, bench, bundle, release, observer, a language server, a debug adapter and an MCP server
editorsA tree-sitter grammar and a VS Code extension

The MCP server lets an agent read a project and check its own work, and since v0.61 it also teaches how Praxis is written: an idiom corpus of wrong-and-right pairs, asked by topic. The repository carries more than the language — Lictor, WebGlue, ports of Cooper, Dextrin and Ichor, and a shelf of libraries. They are on Products.

File types

The extension says which machines a file is for; a namespace can also declare what it needs, such as processes or blocking I/O, and a machine what it offers.

ExtensionFor
.praxevery machine
.praxethe BEAM only
.praxjJavaScript — node and browser
.praxn / .praxbnode only / the browser only
.pxndata, such as the project manifest project.pxn

Where it stands

The current release is v0.63.0 (19 October 2026), the sixty-third minor release since the first commit on 20 August. Phases 0 to 5 are closed: the compiler, runtime and toolchain are written in Praxis and bootstrap themselves, with only a small seed compiler in Elixir left; there are three machines, the BEAM, node and the browser; and the tooling is done and released. The language itself is frozen at v0.56.0.

The work now is an application: Lictor, a moderation tool for live broadcasters, built on WebGlue in the order a user meets it. Each of its journeys pulls from the layers below exactly what it needs, and each is a release of everything it changed. After it come completion, distribution, a stable public Praxis, the book toolchain, the books, and the split into repositories of their own.