Skip to content

Latest commit

 

History

8,428 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Almide

The language where LLM edits survive.

CI License: MIT / Apache-2.0 Ask DeepWiki

Playground · Cheatsheet · Specification · Why · Quick start · Evidence · How it works · Status

An edit that survives

Almide is a statically-typed language built for one metric: modification survival rate — how often code still compiles and passes its tests after a series of AI-driven edits. It compiles to native binaries (via Rust) and to WebAssembly, and the two produce byte-identical output.

The metric in one screen. A model adds a case to a type and, as models do, touches nothing else:

type Shape =
  | Circle(Float)
  | Square(Float)
  | Triangle(Float, Float)   // the edit

fn area(s: Shape) -> Float =
  match s {
    Circle(r) => 3.14159 * r * r
    Square(w) => w * w
  }
error[E010]: non-exhaustive match: missing Triangle(_, _)
  --> shape.almd:7:9
  in match
  here: match s {
  hint: add arms for Triangle(_, _):
  Triangle(arg1, arg2) => _
Or use `_ => todo()` to compile incrementally.

The compiler names the missing case at the site, spells out the arm to add, and offers a way to keep compiling while the rest is written. The model's next turn is Triangle(b, h) => 0.5 * b * h; the program then runs natively and on wasm and prints the same bytes. That loop — an edit, a diagnostic that is itself the fix, a passing build — is what every decision below serves.

Why Almide?

  • Predictable — One canonical way to express each concept, reducing token branching for LLMs
  • Local — Understanding any piece of code requires only nearby context
  • Repairable — Compiler diagnostics guide toward a specific fix, not multiple possibilities (as above)
  • Compact — High semantic density, low syntactic noise

The full rationale: Design Philosophy. The frozen surface and the breaking-change policy: STABILITY.md (declared 2026-08-20) — anything in the Cheatsheet or llms.txt keeps meaning what it means.

Quick Start

Try it in your browser → — no installation.

curl -fsSL https://raw.githubusercontent.com/almide/almide/main/tools/install.sh | sh   # macOS / Linux
irm https://raw.githubusercontent.com/almide/almide/main/tools/install.ps1 | iex        # Windows (PowerShell)

From source, with Rust 1.94+ (the binary embeds the wasmtime host): cargo build --release && cp target/release/almide ~/.local/bin/.

fn main() -> Unit = {
  println("Hello, world!")
}
almide run hello.almd                 # native
almide run hello.almd --target wasm   # same bytes, on wasmtime

Features

  • Multi-target — Same source compiles to a native binary (via Rust) or WebAssembly (direct emit, no LLVM)
  • Generics — Functions (fn id[T](x: T) -> T), records, variant types, recursive variants with auto Box wrapping
  • Pattern matching — Exhaustive match with variant destructuring
  • Effect functionseffect fn for explicit error propagation: expr! propagates, a bare fallible call is an error, never silent
  • Bidirectional type inference — Annotations flow into expressions (let xs: List[Int] = [])
  • Codec systemType.decode(value) / Type.encode(value) with auto-derive
  • Map literals["key": value], m[key], for (k, v) in m
  • Fan — structured concurrency: fan { a(); b() } on real threads natively, sequential on wasm; fan.map / fan.any deterministic by list order on both
  • Pipeline operatordata |> transform |> output
  • Module system — Packages, sub-namespaces, visibility control, diamond dependency resolution
  • Standard library — self-hosted .almd modules: string, list, map, json, http, fs, and more (reference; the count is derived under Project Status)
  • Built-in testingtest "name" { assert_eq(a, b) } with almide test

What is measured

Every claim in this section is either derived by a script or carries the date it was measured; scripts/check-readme-numbers.sh refuses a bare number in CI.

LLM writability

Measured by almide-dojo across 30 tasks (basic / intermediate / advanced) on 2026-04-12; later runs are on the live dashboard:

Model Pass Rate 1-Shot Rate
Claude Sonnet 4.6 100% (30/30) 47%
Llama 3.3 70B 61% (17/28) 33%

The most recent same-model comparison is the MiniGit bench: Sonnet 5 × 20 trials on 2026-07-15, 100% pass, the most concise of 5 languages (233 LOC), and the fastest agent wall-clock against Gleam and MoonBit — an LLM-writability number, measured under 6–9× self-parallelism, not generated-code speed (chart · method · upstream).

Byte-identical across targets

Every program that compiles for both targets produces byte-identical observable output — stdout, stderr, exit code — whether it runs as a native binary or as WebAssembly. Native is the oracle; native == wasm is a hard invariant, not a "target difference" to be documented around.

The guarantee is continuous, with an explicit, ledger-managed scope: "byte-identical" means the execution output, not the compiled artifacts; inherently nondeterministic sources certify deterministic invariants instead of exact bytes; APIs not yet implemented on wasm are compile- or run-time refusals — never wrong bytes; and exactly two fns are exempt because their job is to report the host — env.os() and env.temp_dir(), bounded by C-189, since making them agree across targets would be the defect rather than the guarantee.

This claim is not prose. Every observable promise is a named contract in the behavior-contract ledger, each traceable to executable evidence, and the numbers below are regenerated from the ledger (scripts/gen-claims.sh, enforced by scripts/check-contracts.sh in CI):

Ledger: 347 contracts — 347 active, 0 flagged-for-revision.

Divergences awaiting a fix: none. Every contract in the ledger is active, carrying executable evidence of class >= fixture. The one by-design carve-out in the law — the platform-reporting fns env.os and env.temp_dir — is bounded by C-189.

Scope, ledger mechanics, and the evidence stack (contract ledger, cross-target fixture gate, differential fuzz, emit-time Σ-probes, Lean belt, org-wide byte-verify sweep): docs/design/EQUIVALENCE.md.

Memory safety — proven where it is proven, trusted where it is trusted

You write no ownership annotations, no lifetimes, no free: Perceus-style ownership inference in the compiler decides where every heap value is introduced, duplicated, and consumed — garbage-collector-free, pause-free. On the incumbent wasm leg that decision ships with a per-build ownership certificate a kernel-proven checker re-verifies (Rocq/Coq spine, 96 audited theorems and lemmas, axiom-clean, independently re-checked by coqchk; the count is asserted by proofs/check.sh). The structural wasm leg (the default since #1599) and the native leg are trusted, not proven: their evidence is differential — byte-identical output against the certified leg on the contract corpus, held by a grow-only floor and a semantic-mutation net. The Built … line names the leg that produced your bytes. The boundary, stage by stage: proven-vs-trusted.md; the full account, including the Lean 4 Perceus belt the design started from: docs/design/MEMORY-SAFETY.md.

Performance

No runtime, no GC, no interpreter — native compiles through Rust to machine code, and WASM is emitted directly as self-contained modules.

Program (almide build --target wasm, verified, as shipped) incumbent v1 leg structural leg
Hello, world 1,096 B 1,337 B

Measured on almide 0.62.0, 2026-09-08, from docs/benchmarks/wasm-size.txt; no post-hoc optimizer touches the shipped bytes (--wasm-opt is opt-in and its output is not the verified module).

Rust on the same wasm target is 40 KB+ for Hello, world even fully size-tuned; the native minigit CLI binary is 418 KB stripped with 0 dependencies. The byte-by-byte dissection, measured 2026-07-23 on the incumbent leg: docs/wasm/WASM-OUTPUT.md.

Against handwritten Rust the arithmetic kernels sit at parity (n-body, spectral-norm 1.00×; the ratchet's anchored rows). Where Almide has information Rust does not — a tree whose whole lifetime is one check(make(depth)) expression, proven by the effect system — it is faster than the ordinary Rust for the same program:

Workload (bench.py, median of 9, interleaved) optimization Almide / ordinary Rust without it (ALMIDE_REGION_OFF=1) CI runner
binarytrees region window (#1991) 0.35 (d17) / 0.32 (d19) 1.25 0.61
treealloc region window (#1991) 0.30 (d20) / 0.30 (d21) 1.10 0.61 (est.)

Two ratios per row are the two input sizes (the win holds at both); the Rust side is the ordinary program a person writes for it — a Box per node, one thread, no arena, no unsafe, no SIMD — compiled with the same rustc flags, and the "without it" column is the same Almide source with the region window turned off, so the whole gap is that one optimization. The absolute ratio is allocator-dependent (the CI runner frees a Box cheaper), the direction is not: the perf-ratchet job fails if either row reaches 1.0 or the ablation stops paying. Declaration and methodology: docs/project/BENCHMARKS.md. Ledger: docs/benchmarks/native-victory.txt (almide 0.62.0, 2026-09-08).

Measured on almide 0.59.1, arm64 Darwin, examples/lisp.almd (268 lines), 2026-08-27. Every row is an N-run MEAN — a single run of a 30ms process is scheduler noise. Cold clears BOTH $TMPDIR/almide-run and the dependency cache before each repetition; clearing only the latter measures a warm build. Regenerate with almide run tools/almide-gates/src/main.almd -- bench; the ratchet (-- bench --check) fails CI at 1.5x.

scenario time runs
almide check 15.2 ms 20
build, warm (content-cache hit) 237.2 ms 5
build, cold 635.3 ms 3
build, cold, --target wasm 61.7 ms 3

almide check scales linearly: over a 2k → 30k-line ladder of this repo's own stdlib the log-log slope of check time against project lines is 1.13 (1.0 is linear, 2.0 quadratic) and the 10k-line rung costs 4.4× the empty-project floor — measured 2026-08-13, held by scripts/check-edit-loop-scale.sh, table in BENCHMARKS.md. Native runtime against handwritten Rust: 1.00× on n-body and spectral-norm, 1.16–1.18× on fasta and FFT, ~1.6× where the workload is list materialization (#1004), CI-gated ratio ratchet (scoreboard). Wasm runtime, measured and gated (#1701):

Benchmark (almide bench, verify-then-time, median of 5) wasm/native ratio
nbody 2.69×
spectralnorm 2.65×
binarytrees 0.93×
treealloc 0.41×
fft 4.02×
strchurn 1.15×
listbuild_append 3.33×
listbuild_combinator 3.79×
listbuild_prealloc 3.54×
mapbuild 1.16×

Embedded wasm host (Perceus RC in linear memory) against the native binary, same machine, same run. Cross-engine ratios do NOT cancel hardware (a 2-core CI runner measures nbody ~10x worse), so the ratio verdict runs on the stamping machine class and CI gates the STATUS taxonomy below (scripts/check-wasm-runtime-ratio.sh). binarytrees runs its fan arms on the embedded host's thread pool, which is why wasm WINS there. The unmeasured corpus cells stay honest instead of estimated: 3 route to the incumbent artifact, 1 wall on the wasm build path, 0 exhaust the embedded heap (#1729) — each re-measured every gate run, so a cell that starts benching fails the gate until its row is promoted. Ledger: docs/benchmarks/wasm-runtime.txt (almide 0.62.0, 2026-09-08).

How It Works

One frontend, one IR, three renderers behind two targets:

flowchart LR
    SRC([".almd"]) --> FE["Lexer → Parser → Type Checker → Lowering"] --> IR(["IR"])
    IR --> NANO["Nanopass Pipeline<br/>semantic rewrites"] --> TMPL["Template Renderer<br/>TOML-driven"] --> RS([".rs → native binary"])
    IR --> ROUTER{"router"}
    ROUTER --> STRUCT["structural leg<br/>commissioned engine, direct emit"] --> WASM([".wasm"])
    ROUTER --> INCUMB["incumbent v1 leg<br/>certified MIR, direct emit"] --> WASM
Loading

Native. The Nanopass pipeline applies target-specific transformations — ResultPropagation (Rust ?), CloneInsertion (Rust borrow analysis), LICM (loop-invariant code motion). The Template Renderer is purely syntactic: every semantic decision is already encoded in the IR.

WebAssembly. Since commissioning (#1599) two verified renderers sit behind one router (render_wasm_module_routed in src/cli/build.rs). The structural leg — the commissioned engine, almide::wasm_leg front + crates/almide-wasm emitter — takes every program with a main, no external packages, and no host-variant I/O on the build path; it was accepted at 610/610 byte-identical to native on the wasm_cross corpus, and its build artifacts ship in the WASI form (#1588) so they run on stock runtimes. The incumbent v1 leg — the certified MIR trust spine in crates/almide-mir — takes main-less library modules, dependency-bearing projects, host-variant programs, and any shape the structural leg walls on: a verified-to-verified handover, never the retired unverified emitter, and a program neither leg lowers is an honest error. ALMIDE_WASM_INCUMBENT=1 forces the incumbent; ALMIDE_VERIFIED_DEBUG=1 narrates the routing.

almide run app.almd                  # Compile + execute (native)
almide build app.almd --target wasm  # Build WebAssembly (WASI)
almide test                          # Find and run all test blocks (recursive)
almide check app.almd                # Type check only
almide check app.almd --target wasm  # + the wasm build route: E081/E082 at check time (#1922)
almide fmt app.almd                  # Format source code

Run almide --help for the full command list (compile, add, deps, clean, …). Pipeline and module map: docs/ARCHITECTURE.md; the two wasm legs in detail: docs/wasm/.

What's next — v1, the Trust Spine

The Perceus proof above proves one compiler pass, once. v1 generalizes that principle to the whole pipeline — instead of proving the 100k-line compiler, it proves a tiny checker and has the compiler emit a certificate on every build that the checker re-verifies. If the checker accepts, the artifact has the property — a theorem that never mentions the compiler's internals. That collapses the trusted base from ~100,000 lines to the extracted checker (~1,400 lines of OCaml, machine-derived from the proofs), and asks a harder question than testing ever can: not "do the tests pass?" but "can a machine prove the output is correct?" The architecture, the receipts (C-SAFE / C-REPRO / C-FAITHFUL / C-PROVEN), and why builds are slower on purpose: docs/TRUST-SPINE.md.

Project Status

Category Status
Maturity Pre-1.0, under active development on develop; the LLM-facing surface is frozen by STABILITY.md (declared 2026-08-20)
Support Latest release line only, pre-1.0 — policy and versioning guarantees: SUPPORT.md · vulnerabilities: SECURITY.md
Compiler Pure Rust, single binary, 0 ICE
Targets Rust (native), WASM (direct emit — two verified legs behind one router, see How It Works)
Verified codegen Incumbent v1 leg: PCC certificates re-verified on every build since 0.29.0 (--no-verified opts out). Structural leg: byte-exact corpus and mutation gates, no certificate yet
Codegen Rust: Nanopass + TOML templates; wasm: structural engine or certified MIR → direct emit (the unverified v0 emitter is retired — a wall is an error, never a fallback)
Artifacts .almdi module interface files via almide compile
Playground Live — the compiler runs as WASM in the browser
Derived count Value
Stdlib 985 functions across 43 modules — self-hosted .almd, signature indexes regenerated from the compiler by tools/gen-stdlib-doc-index.py
Tests 436 .almd test files under spec/ (almide test spec/) + the 347-contract cross-target ledger

Ecosystem and documentation

Contributing

Issues and pull requests are welcome on GitHub. After cloning, install the git hooks (brew install lefthook && lefthook install); commits must be in English (enforced by the commit-msg hook). Project conventions: CLAUDE.md.

License

Licensed under either of MIT or Apache 2.0 at your option.

About

A statically-typed programming language optimized for LLM code generation. Compiles to Rust and WebAssembly.

Topics

Resources

Security policy

Stars

32 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages