Documentation

Language Reference

These documents define Elle's syntax, semantics, and type system.

DocumentDescription
language.mdComplete language reference: syntax, data types, variables, functions, control flow, scoping
types.mdType system: the mutable/immutable split, all types, predicates, display format, equality
semantics.mdAuthoritative semantics: truthiness, lists, conditionals, equality, destructuring
macros.mdMacro system: current state, architecture, hygiene, scope sets
modules.mdModule system: closure-as-module, parametric imports, qualified symbols, trade-offs

Design Documents

Architecture and design rationale for Elle's core systems.

DocumentDescription
signals.mdSignal system: motivation, signal protocol, signal inference, JIT integration
fibers.mdFiber architecture: execution contexts, signals, suspension/resumption, parent/child chains
ffi.mdFFI design: type descriptors, signatures, calling C functions, callbacks, marshalling
epochs.mdEpoch versioning system

Contributor Guides

Practical guides for working on the Elle codebase.

DocumentDescription
cookbook.mdStep-by-step recipes for common changes: new primitives, heap types, bytecode instructions, special forms, lint rules, macros
testing.mdTesting strategy: decision tree, test categories, property tests, CI structure, running tests
pipeline.mdCompilation pipeline: entry points, VM ownership, expander lifecycle, fixpoint loop, caching
debugging.mdDebugging toolkit: introspection primitives, time API, signal system, memory profiling
oddities.mdIntentional 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: 4

Fibers

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))  # => 3

Macros

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")