ἐφάπαξ — once for all
A linear type system for safe memory management targeting WebAssembly.
Ephapax uses a compact, statement-delimited block syntax. Newlines act as
statement separators (equivalent to ;).
// Function declaration
fn main() -> i32 {
let x = 1 + 2;
let y = x * 3;
y
}
// Linear let-binding
let! z = copy(x) in zGrammar highlights:
-
Functions:
fn name(params) → Ty { … }orfn name(params) : Ty { … } -
Let bindings:
let x = expr in expr -
Linear let bindings:
let! x = expr in expr -
Block expressions:
{ expr1; expr2; …; exprN }(final expr is the result) -
Comments:
//to end-of-line
just idris-build
just smokeThis compiles examples/hello.eph end-to-end and produces a WASM artifact.
-
The WASM backend currently assumes
i32representation for pairs/sums. -
Lambda/app compilation is stubbed (closure conversion not yet implemented).
Key interfaces where correctness and stability matter:
-
Idris2 → S-expression IR (
Ephapax.IR.Decodeencoding/decoding). -
Zig FFI token buffer → Idris2 parser stream (
Ephapax.Parse.ZigBuffer). -
S-expression IR → Rust/WASM backend (
ephapax-irandephapax-wasm).
We now include the proven library as a dependency to harden string handling
paths over time (e.g., JSON-style escaping for IR boundaries).
Ephapax is a programming language with a linear type system that guarantees:
| Guarantee | Mechanism |
|---|---|
No use-after-free |
Linear resources cannot be accessed after consumption |
No memory leaks |
Linear resources must be consumed exactly once |
Region-based deallocation |
Bulk memory management without garbage collection |
Zero-cost abstractions |
Safety guarantees enforced at compile time |
| Principle | Mechanism |
|---|---|
Linearity |
Every linear value used exactly once |
Regions |
Scoped allocation with bulk deallocation |
Second-class borrows |
Temporary access without ownership transfer |
Explicit copies |
Duplication requires explicit syntax |
region r:
let s1 = String.new@r("hello, ")
let s2 = String.new@r("world")
-- Borrow s1 to get length (does not consume)
let len = String.len(&s1)
-- Concatenation consumes both strings
let result = String.concat(s1, s2)
-- result is consumed by returning
result
-- Region exits: any remaining allocations freedephapax/
├── formal/ # Coq formalisation of type system
│ ├── Syntax.v # AST and types
│ ├── Typing.v # Linear typing rules
│ └── Semantics.v # Operational semantics & soundness
├── src/ # Implementation
│ ├── ephapax-syntax/ # AST definitions
│ ├── ephapax-typing/ # Linear type checker
│ ├── ephapax-lexer/ # Tokenizer (logos)
│ ├── ephapax-parser/ # Parser (chumsky)
│ ├── ephapax-interp/ # Tree-walking interpreter
│ ├── ephapax-wasm/ # WASM code generation
│ ├── ephapax-runtime/ # Runtime support
│ ├── ephapax-repl/ # Interactive shell
│ └── ephapax-cli/ # Command-line interface
├── conformance/ # Type system conformance tests
│ ├── pass/ # Programs that should type-check
│ └── fail/ # Programs that should be rejected
├── spec/ # Language specification
├── .machine_read/ # Machine-readable specs for tooling
└── docs/ # Documentation| Target | Description | Status |
|---|---|---|
WebAssembly |
|
Primary |
Native |
Via Cranelift |
Secondary |
The type system is grounded in:
-
Intuitionistic linear logic — Resource-sensitive reasoning
-
Separation logic — Memory ownership and framing
-
Region calculus (Tofte-Talpin) — Scoped allocation
See formal/ for Coq mechanisation.
-
Rust 1.83+ with
wasm32-unknown-unknowntarget -
Coq 8.18+ (optional, for proof verification)
-
just (task runner)
# Build all crates
just build
# Build for WASM
just build-wasm
# Build Idris2 affine stage
just idris-build
# Verify Coq proofs (optional, requires Coq 8.18+)
just proofsEphapax includes an experimental two-stage pipeline:
-
Stage 1 (Idris2): parse concrete Ephapax syntax (or S-expression IR) and type-check in affine or linear mode.
-
Stage 2 (Rust): compile the checked IR to WebAssembly.
Run:
# Compile a module through Idris2 + WASM backend (concrete syntax)
just compile-affine input.eph affine output.wasm
# Compile an S-expression module through Idris2 + WASM backend
scripts/compile-affine.sh input.sexpr --mode affine --out output.wasm --sexpr
# Or call the CLI directly for Stage 2
cargo run -p ephapax-cli -- compile-sexpr input.sexpr -o output.wasm🚧 Early Development
| Component | Status |
|---|---|
Core type system design |
✅ Complete |
Formal semantics (Coq) |
✅ Complete |
Lexer |
✅ Complete |
Parser |
✅ Complete |
Type checker |
🚧 In Progress |
WASM code generation |
🚧 In Progress |
Interpreter |
✅ Complete |
REPL |
✅ Complete |
CLI |
✅ Complete |
Standard library |
🔲 Planned |
-
Rust — Ownership and borrowing
-
Linear Haskell — Linear types in GHC
-
MLKit — Region-based memory management
-
Cyclone — Safe C dialect with regions
Contributions are welcome! Please see:
EUPL-1.2 — See LICENSE.txt