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.60

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 PRX0128 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), docs, release, a language server, a debug adapter and an MCP server
editorsA tree-sitter grammar and a VS Code extension

Libraries written in Praxis include ports of Cooper for configuration, Dextrin for DXN data, Ichor for grammars, the test framework Touchstone — and Webglue, a web framework.

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.60.0 (22 September 2026) — sixty releases since the first commit on 20 August. The compiler, runtime and toolchain are written in Praxis and bootstrap themselves; only a small seed compiler in Elixir remains. The language itself is frozen at v0.56.0: its forms and shapes are settled, while the toolchain keeps moving. The work now is a book — the tutorial — and after it, distribution: publishing packages, an installer, and a stability commitment on the way to 1.0.