Web UIs, written in Lean 4.
A model, an update function and a view: plain Lean values, type-checked like the rest of your code. LeanUI compiles them to WebAssembly (wasm64), drives the DOM with a compact command buffer, and renders the same program on a Lean server.
def view (n : Int) : Html Msg :=
<main class="counter">
<h1>LeanUI counter</h1>
<p>
<button id="dec" onClick={.dec}>−</button>
<output id="value" class={if n < 0 then "neg" else "pos"}>{n}</output>
<button id="inc" onClick={.inc}>+</button>
</p>
<p>
<input id="box" type="number" value={toString n} onInput={fun s => .set (s.toInt?.getD n)} />
<button id="reset" onClick={.reset}>reset</button>
</p>
</main>That is the view of examples/Counter.lean, running here:
LeanUI counter
Pure and typed
Views are values of type Html Msg. Templates are checked by Lean's elaborator and expand to the constructors you would write by hand, so they cost nothing at run time.
Fast where it counts
A keyed, exact-lazy diff that recognises unchanged subtrees without allocating; about 1.3× the CPU time of hand-written JavaScript on the standard benchmark.
Proved, then tested
The keyed reconciliation planner is proved correct and optimal in Lean; property tests and browser fuzzing check the rest against a reference model.
Everywhere
This page was rendered to HTML by a Lean server, then hydrated by wasm. Browsers without Memory64 get the same app driven from the server.