Dhall · C11 · one source, many targets

A Dhall subset,
written in C.

Typecheck, normalize, and serialize Dhall configuration. The same C interpreter ships two ways: a single native binary that runs on every major OS, and a WebAssembly build that runs right here in your browser.

One source · two targets

Compiled once for C.
Delivered two ways.

The same lexer, parser, normalizer, and typechecker (src/*.c) build into two artifacts that cover the entire spectrum — from your terminal to a browser tab.

Native binary — dhall.com

APE · cosmocc

A single self-contained ~1 MB polyglot binary — an Actually Portable Executable built with cosmocc (Cosmopolitan libc). The same file runs natively, no VM, no runtime, no recompile:

  • Linux
  • macOS
  • Windows
  • FreeBSD
  • NetBSD
  • OpenBSD

…and any other x86-64 platform cosmocc targets.

The full CLI: file and env: imports, and http:// URL imports with sha256: verification and SSRF-safe fetch.

$ ./dhall.com to-json config.dhall
$ make          # builds dhall.com
Build & usage

In the browser — dhall.wasm

WebAssembly

The same interpreter compiled to a 122 KB .wasm module (dhall.js + dhall.wasm). It runs 100% client-side — no server, no build step, no upload. Your config never leaves the tab.

File imports are unavailable (no filesystem in the browser), but http:// imports work — fetched live with the browser's fetch (subject to CORS) and verified by their mandatory sha256: hash.

$ make wasm     # → docs/dhall.js + dhall.wasm
$ node tests/wasm-smoke.js
Try the live demo ↓

WebAssembly · runs in this tab

Live demo

Pick a mode, then edit the source. Press Run or Ctrl/⌘+Enter. The editor is a real CodeMirror with Dhall syntax highlighting, and the actual dhall-lsp server (the same src/lsp.c, compiled to wasm) runs behind it — live publishDiagnostics squiggles as you type, and hover shows the inferred type. You can even paste an http://… import with a sha256: hash — the browser fetches it live (cross-origin requests need CORS headers on the target).

dhall — in your browser wasm
loading LSP wasm…
loading wasm…

What it does

A capable Dhall subset

A single-term de Bruijn core, eager normalization, bidirectional typechecking — no shortcuts.

Typecheck

Bidirectional inference with precise Error: … at line N, col M diagnostics.

Normalize

Eager, step-sound normalization over a de Bruijn core; normal forms re-parse and round-trip.

Arbitrary precision

Natural and Integer are unbounded big ints — + − × never overflow, and Natural subtraction saturates at 0.

Imports

./file, env:NAME, and http:// URLs with mandatory sha256: hashes and ? fallback — in the native binary (SSRF-safe) and in the browser (via fetch, CORS permitting).

Serializers

One evaluated value tree → JSON, TOML, and YAML (block style).

Records & unions

Recursive /\ merge, right-biased //, with updates, merge, and toMap — including the empty record.

Lists & builtins

List/map, filter, fold, build, length, head, last, indexed, reverse, plus Natural/build and Optional/fold.

assert

assert : type-level checks, enforced in every mode — typecheck, normalize, and all three serializers.

Unicode operators

λ are accepted alongside their ASCII spellings.

Round-trip Doubles

IEEE 754 Double printed with the shortest representation that round-trips; non-finite maps to null/nan/inf.

Real configs

Examples

Click one to load it into the demo (fetched from examples/).

Performance

Benchmark

Representative per-phase timings over a ~20-field nested record, a 200-element list, a let/lambda, and a merge. Measured with ./bench.com.dbg (in-process, x86-64, cosmocc).

phasens/opops/s
parse23,13043,235
normalize285,4453,503
typecheck5,246190,618
serialize (to-json)22,40844,627

Built with make bench; timings are a measurement tool, not a correctness check.

Build & run

Quickstart

Requires cosmocc (Cosmopolitan toolchain). Build the native APE and run the full suite:

Native — make

make            # dhall.com (APE) + dhall.com.dbg (ELF)
make test       # run.sh + roundtrip.sh + examples.sh + cli.sh
make bench      # in-process benchmark

Browser — make wasm

make wasm               # → docs/dhall.js + dhall.wasm
node tests/wasm-smoke.js # headless smoke test

Needs emscripten clang lld llvm.

Usage

dhall typecheck [file|-]   # infer the type of an expression
dhall normalize [file|-]   # print the normal form
dhall to-json   [file|-]   # evaluate to JSON
dhall to-toml   [file|-]   # evaluate to TOML (top level must be a record)
dhall to-yaml   [file|-]   # evaluate to YAML (block style, 1.2 core schema)