Documentation
Language Reference
These documents define Elle's syntax, semantics, and type system.
| Document | Description |
|---|---|
| language.md | Complete language reference: syntax, data types, variables, functions, control flow, scoping |
| types.md | Type system: the mutable/immutable split, all types, predicates, display format, equality |
| semantics.md | Authoritative semantics: truthiness, lists, conditionals, equality, destructuring |
| macros.md | Macro system: current state, architecture, hygiene, scope sets |
| modules.md | Module system: closure-as-module, parametric imports, qualified symbols, trade-offs |
Design Documents
Architecture and design rationale for Elle's core systems.
| Document | Description |
|---|---|
| signals.md | Signal system: motivation, signal protocol, signal inference, JIT integration |
| fibers.md | Fiber architecture: execution contexts, signals, suspension/resumption, parent/child chains |
| ffi.md | FFI design: type descriptors, signatures, calling C functions, callbacks, marshalling |
| epochs.md | Epoch versioning system |
Contributor Guides
Practical guides for working on the Elle codebase.
| Document | Description |
|---|---|
| cookbook.md | Step-by-step recipes for common changes: new primitives, heap types, bytecode instructions, special forms, lint rules, macros |
| testing.md | Testing strategy: decision tree, test categories, property tests, CI structure, running tests |
| pipeline.md | Compilation pipeline: entry points, VM ownership, expander lifecycle, fixpoint loop, caching |
| debugging.md | Debugging toolkit: introspection primitives, time API, signal system, memory profiling |
| oddities.md | Intentional design oddities and their rationale |
Signals
Signals are typed, cooperative flow-control interrupts. A signal is a keyword — :error, :log, :abort, or any user-defined name — that a fiber emits to its parent. The parent's signal mask determines which signals surface; unmasked signals propagate further up. See signals.md for the full design document.
The compiler infers which functions can emit signals and enforces that silent contexts don't call yielding ones.
Signal example — early termination with :abort
# Declare a user-defined signal
(signal :abort)
# A function that signals early termination
(defn find-first [pred xs]
(each x in xs
(when (pred x)
(emit :abort x))))
# Drive it from a fiber
(def f (fiber/new (fn [] (find-first even? [1 3 5 4 7])) |:abort|))
(fiber/resume f)
(print "found:" (fiber/value f)) # => found: 4Fibers
A fiber is an independent execution context — its own stack, call frames, signal mask, and heap. Fibers are cooperative and explicitly resumed. When a fiber finishes, its entire heap is freed in O(1). See fibers.md for the full architecture reference.
Fiber example — yielding values one at a time
(defn produce []
(emit :yield 1)
(emit :yield 2)
(emit :yield 3))
(def f (fiber/new produce |:yield|))
(fiber/resume f) (print (fiber/value f)) # => 1
(fiber/resume f) (print (fiber/value f)) # => 2
(fiber/resume f) (print (fiber/value f)) # => 3Macros
Elle has fully hygienic macros built on scope sets. Macros run at compile time and produce syntax objects that respect lexical scope. See macros.md for the full design document.
Macro example — timing an expression
(defmacro time-it [expr]
(def g (gensym))
~(let ((,g (clock/now)))
,expr
(print "elapsed:" (- (clock/now) ,g))))
(time-it (fold + 0 (range 10000)))FFI
Elle can call C functions directly. ffi/native loads shared libraries and ffi/defbind creates typed bindings with automatic marshalling. See ffi.md for the full architecture reference.
FFI example — calling libc puts
(def libc (ffi/native nil))
(ffi/defbind libc puts [:string] :int)
(puts "hello from C")