Language
Eta — Language Guide
Note
This guide is the canonical entry point for learning the Eta language. Each section is intentionally short and links to a deep-dive page — either a tutorial chapter under
docs/guide/or a module / tool reference underdocs/guide/reference/.
Note
Looking for the library APIs? See the Standard Library Guide — a per-module reference covering
core,collections,math,aad,torch,causal,clp,logic,net,process, and the rest of thestd.*packages.
1. Orientation
Eta is a Lisp/Scheme-like language with a hygienic macro system and a
stack-based bytecode VM that uses NaN-boxing for value representation.
The same VM hosts a wide range of capabilities — the symbolic core,
logic programming, constraint logic programming (CLP), automatic
adjoint differentiation, statistics, neural networks, causal inference,
and actor-style concurrency — delivered as first-class language features
or as packaged std.* modules.
Toolchain
| Tool | Role | Reference |
|---|---|---|
etai | Interpreter for .eta source or .etac bytecode | Quick Start |
etac | Ahead-of-time bytecode compiler | Compiler, Bytecode Tools |
eta_repl | Interactive REPL | REPL |
eta_lsp | Language Server (diagnostics, completion, navigation) | VS Code, Debugging |
eta_dap | Debug Adapter (breakpoints, stepping, inspection) | Debugging |
eta_test | Test runner with TAP / JUnit output | Testing |
eta_jupyter | Jupyter kernel | Jupyter |
eta prof | Runtime profiling (run, report, merge, view) | Profiling |
Hello, world
(module hello
(import std.io)
(begin
(println "Hello, world!")))
etai hello.eta
Tip
The interactive notebook at
cookbook/notebooks/LanguageBasics.ipynbwalks through the same material as §§ 2–9 in a runnable form.
How to read this guide
Each chapter is short, with a small example and a Deep dive link. Read the chapters in order for a tour, or jump straight to the deep dive of the topic you need.
2. Syntax & Values
Eta source is S-expressions: lists ( … ), vectors #( … ), strings
"…", characters #\a, booleans #t / #f, symbols, fixnums and
floats. Comments are ; to end of line, or #| … |# for block
comments. Quoting uses ', quasi-quotation `, unquote ,, and
splicing ,@.
'(1 2 3) ; list literal
#(1 2 3) ; vector literal
`(a ,(+ 1 2) ,@'(c d)) ; => (a 3 c d)
Equality has three flavours: eq? (identity), eqv? (numeric/char
equivalence), equal? (structural). The numeric tower is fixnum +
double, with NaN-box tagging — see nanboxing.md.
Because code is just S-expression data, eval compiles and executes
any expression at runtime against the current lexical environment.
This is fundamental: it is how the symbolic engine in cookbook/xva-wwr
lowers a differentiated expression onto the active AAD tape, how the
REPL evaluates user input, and how macros and quasi-quotation compose
with runtime metaprogramming.
(eval '(+ 1 2)) ; => 3
(let ((x 10)) (eval '(+ x 5))) ; => 15
(define f (eval '(lambda (a b) (* a b)))) ; build a closure from data
(f 3 4) ; => 12
Deep dives:
syntax-and-values.md,eval.
3. Bindings & Scope
Eta is lexically scoped. The binding forms are define, defun (a
define + lambda shorthand), let, let*, letrec, named-let,
and set!. Internal defines inside a lambda/let are mutually
recursive.
(define greeting "hello")
(let* ((a 1)
(b (+ a 1))
(c (+ a b)))
c) ; => 3
(let loop ((n 10) (acc 1)) ; named let
(if (= n 0) acc (loop (- n 1) (* acc n))))
Note
The REPL allows shadowing (re-
defineof an existing name); modules do not. Seerepl.mdfor the REPL-specific rules.
Deep dive:
bindings-and-scope.md.
4. Control Flow
| Form | Purpose |
|---|---|
if | Two-branch conditional |
cond | Multi-branch with optional else |
when / unless | One-armed conditional with implicit begin |
case | Dispatch on eqv? of a key |
and / or | Short-circuit, return the deciding value |
begin | Sequence expressions, return the last |
Loops are recursive: use named let for tight loops and letrec for
mutual recursion. All such forms are tail-call optimised — see §5.
(cond
((null? xs) 'empty)
((= (length xs) 1) 'singleton)
(else 'many))
Deep dive:
control-flow.md.
5. Functions, Closures & Tail Calls
lambda constructs a closure; defun is shorthand for
(define name (lambda …)). Parameter lists support fixed, optional and
dotted-rest arguments, and apply calls a function with a list of
arguments.
(defun adder (n) (lambda (x) (+ n x))) ; closure over n
(define add5 (adder 5))
(add5 3) ; => 8
(defun sum (first . rest) (foldl + first rest))
(apply sum '(1 2 3 4)) ; => 10
Eta guarantees tail-call optimisation in tail position — including the
last expression of if, cond, when, unless, case, let*,
letrec, and begin. Mutual recursion via letrec runs in constant
stack.
Deep dives:
functions-and-closures.md,tail-calls.md.
6. Records & Compound Data
define-record-type produces a constructor, a predicate, accessors and
optional setters. Records are not pairs; equal? performs structural
comparison.
(define-record-type <point>
(make-point x y)
point?
(x point-x)
(y point-y set-point-y!))
(define p (make-point 3 4))
(point-x p) ; => 3
(set-point-y! p 7)
Other compound types: pairs / lists, vectors (#( … ), fixed-length,
mutable, O(1) indexed), hash maps and hash sets — see §12.
7. Pattern-Style Dispatch
Eta has no built-in match form. Idiomatic dispatch uses cond with
predicate guards or, for symbolic data, structural unification from
std.logic (§17).
(defun shape-area (s)
(cond
((and (pair? s) (eq? (car s) 'circle))
(let ((r (cadr s))) (* 3.14159 r r)))
((and (pair? s) (eq? (car s) 'rect))
(* (cadr s) (caddr s)))
(else (raise 'unknown-shape s))))
For destructuring on relational data, use (== pat term) with logic
variables — see logic.md.
8. Macros (syntax-rules)
Macros are hygienic and pattern-based. define-syntax binds an
expander; syntax-rules lists (pattern → template) cases with
ellipsis (...) for variadic patterns.
(define-syntax swap!
(syntax-rules ()
((_ a b)
(let ((tmp a))
(set! a b)
(set! b tmp)))))
Important
Eta’s macro system is
syntax-rulesonly — there are no procedural macros. This keeps expansion deterministic and serialisable into bytecode. Seemacros.mdfor ellipses, literal keywords, and worked examples from the standard library.
Deep dive:
macros.md.
9. Modules & Imports
Every source file declares one or more (module name … ) forms with
explicit (import …) and (export …) clauses. The module search path
is the input file’s directory plus --path arguments and
ETA_MODULE_PATH.
| Clause | Effect |
|---|---|
(import std.math) | All exports of a module |
(import (only std.math pi e)) | Only listed names |
(import (except std.collections sort)) | All except listed names |
(import (rename std.math (pi PI))) | Rename on import |
(import (prefix std.math math:)) | Namespace-style qualified access |
Reference:
modules.md.
10. Error Handling
raise and catch compile to the Throw and SetupCatch VM opcodes.
Tags are symbols; the raised payload can be any value. A tag-less
(catch body) is a catch-all that also intercepts runtime.* errors.
(defun safe-div (a b)
(if (= b 0)
(raise 'division-by-zero (list a b))
(/ a b)))
(catch 'division-by-zero (safe-div 10 0))
;; => (10 0)
dynamic-wind runs its after thunk on every exit (normal or
exceptional), enabling reliable cleanup.
Deep dive:
error-handling.md.
11. Strings, Symbols & Regex
Strings are immutable byte sequences with the standard Scheme operations
(string-append, string-length, substring, string-ref,
string->list, string->symbol, …). Symbols are interned. Regular
expressions live in std.regex.
(import std.regex)
(define re (regex:compile "(\\d+)-(\\d+)"))
(regex:find-all re "10-20 and 30-40")
;; => (("10-20" "10" "20") ("30-40" "30" "40"))
Deep dive:
strings.md. Reference:regex.md.
12. Collections
| Container | Mutability | Indexing | Module |
|---|---|---|---|
| List | immutable | O(n) | builtin |
| Vector | mutable | O(1) | builtin |
| Hash map | mutable | O(1) avg | std.hashmap |
| Hash set | mutable | O(1) avg | std.hashset |
| Fact table | columnar | indexed | std.fact_table |
std.collections provides the higher-order suite (map*, filter,
foldl / foldr, reduce, zip, range, take / drop,
flatten, sort, any?, every?, …).
(import std.prelude)
(foldl + 0 (map* (lambda (x) (* x x)) (filter even? (range 1 11))))
;; => 220
Deep dive:
collections.md.
13. I/O, Filesystem & OS
Built-ins: display, write, newline, write-string, read-char,
current-{input,output,error}-port, open-input-file,
open-output-file, open-input-string, open-output-string,
get-output-string. std.io adds println, eprintln, read-line,
display->string, and the with-…-port redirection helpers.
(import std.io)
(with-output-to-port (open-output-string)
(lambda () (println "captured")))
CSV via std.csv, Datalog via
std.db. JSON has its own section (§14), and
structured logging has its own section (§15).
Filesystem (std.fs)
std.fs wraps the native std::filesystem-backed builtins for path
manipulation, directory enumeration, and file metadata. Paths are
plain strings; results round-trip through the platform-preferred
separator.
(import std.fs std.io)
(when (fs:directory? "examples")
(for-each println (fs:list-directory "examples")))
(define cfg (fs:path-join (fs:temp-directory) "eta" "config.json"))
(println (fs:path-normalize cfg))
| Function | Purpose |
|---|---|
fs:file-exists? | #t iff the path resolves on disk |
fs:directory? | #t iff the path is a directory |
fs:delete-file | Remove a regular file |
fs:make-directory | Create a directory (idempotent) |
fs:list-directory | Sorted list of entry names (no ./..) |
fs:path-join | Variadic; joins with the platform separator |
fs:path-split | Inverse of fs:path-join (root + components) |
fs:path-normalize | Lexical canonicalisation |
fs:temp-file | Allocate a fresh temp-file path |
fs:temp-directory | Allocate a fresh temp-directory path |
fs:file-modification-time | mtime in epoch milliseconds |
fs:file-size | Size in bytes |
Operating system (std.os)
std.os exposes process-level concerns: environment variables, the
script’s own command line, current working directory, and a clean
exit.
(import std.os std.io)
(println (os:command-line-arguments)) ; e.g. ("--verbose" "in.csv")
(println (or (os:getenv "ETA_HOME") "(unset)"))
(os:change-directory! (os:current-directory))
| Function | Purpose |
|---|---|
os:getenv | Lookup; #f if unset |
os:setenv! / os:unsetenv! | Mutate the process environment |
os:environment-variables | Sorted alist of ("KEY" . "value") |
os:command-line-arguments | List of strings passed to etai / etac |
os:current-directory | Working directory as a string |
os:change-directory! | chdir equivalent |
os:exit | Terminate the process with an optional status code |
Command-line arguments (std.args)
std.args is an argparse-style parser: declare a tuple spec, hand it
argv, and get back a hash map keyed by symbol (with a 'positional
entry for non-option arguments). Supports flag, string, int,
float, and list value kinds; --name value, --name=value,
short flags, and -- to forward the rest as positional. Optional
parse / validate lambdas, choices, required?, and a count
action cover the awkward cases.
(import std.args std.io)
(define spec
'((verbose (--verbose -v) flag)
(out (--out -o) string "a.out")
(jobs (--jobs -j) int 1)
(tag (--tag) list)))
(define r (args:parse-command-line spec))
(when (args:get r 'verbose) (println "loud mode"))
(println (args:get r 'positional))
Runnable demo: cookbook/basics/args.eta.
Subprocesses (std.process)
std.process runs and controls external OS processes. Use
process:run for the common shell-out (blocking, captures stdout /
stderr) and process:spawn for long-running children whose stdio you
want to drive as Eta ports.
(import std.process std.io)
;; Blocking — returns (exit-code stdout stderr)
(define r (process:run "git" '("--version")))
(println (cadr r)) ; "git version 2.45.0\n"
;; Non-blocking — drive the child by its ports
(define p (process:spawn "python" '("-u" "-c" "print(input())")))
(display "hello\n" (process:stdin-port p))
(close-port (process:stdin-port p))
(println (read-line (process:stdout-port p))) ; "hello"
(process:wait p) ; => 0
| Function | Purpose |
|---|---|
process:run program args [opts] | Block; return (exit-code stdout stderr) |
process:spawn program args [opts] | Start a child; return a process handle |
process:wait / process:kill / process:terminate | Lifecycle (graceful or hard stop) |
process:pid / process:alive? / process:exit-code | Status queries |
Options accepted by both run and spawn: cwd, env,
replace-env?, stdin, stdout, stderr, timeout-ms, binary?.
Reference:
process.md.
Deep dive:
io.md. References:fs.md,os.md,args.md,process.md.
14. JSON
std.json is a thin wrapper over the native JSON codec implemented in
eta/core/src/eta/util/json.h (a hand-written, RFC 8259 parser and
serialiser with no third-party dependency). Objects decode to hash
maps, arrays to vectors, numbers default to flonums (pass
'keep-integers-exact? #t to keep integer-typed numbers as fixnums),
true / false to #t / #f, and null to '().
(import std.json std.io)
(define cfg (json:read-string "{\"name\":\"eta\",\"n\":7}"
'keep-integers-exact? #t))
(println (hash-map-ref cfg "name")) ; "eta"
(println (hash-map-ref cfg "n")) ; 7
(println (json:write-string (hash-map "ok" #t "xs" #(1 2 3))))
;; => {"ok":true,"xs":[1,2,3]}
| Function | Signature | Notes |
|---|---|---|
json:read | (port [opts ...]) -> value | Reads from any input port. |
json:read-string | (string [opts ...]) -> value | Convenience for in-memory text. |
json:write | (value [port]) -> '() | Defaults to (current-output-port). |
json:write-string | (value) -> string | Returns the serialised form. |
Options are alternating keyword/value pairs; the only key recognised
in v1 is 'keep-integers-exact?.
Reference:
json.md.
15. Logging
std.log is the structured-logging façade over the bundled
spdlog runtime. Build a sink, wrap
it in a named logger, and emit records at one of six severity levels;
records carry a free-form message plus an optional payload alist that is
rendered either as key=value (human formatter) or as JSON.
(import std.log)
(let* ((sink (log:make-stdout-sink))
(logger (log:make-logger "app" (list sink))))
(log:set-default! logger)
(log:info "service started" '((port . 8080)))
(log:warn logger "slow query" '((ms . 412)))
(log:flush! logger))
Each level wrapper (log:trace, log:debug, log:info, log:warn,
log:error, log:critical) accepts (msg), (msg payload),
(logger msg), or (logger msg payload).
| Helper | Purpose |
|---|---|
log:make-logger | Create a named logger fanning out to one or more sinks |
log:make-stdout-sink / …-stderr-sink | Coloured console sink (toggle with 'color? #f) |
log:make-file-sink | Plain file sink ('truncate? #t for fresh files) |
log:make-rotating-sink | Size-rotated file (bytes per file, retained file count) |
log:make-daily-sink | Daily-rotated file at (hour, minute) local time |
log:make-port-sink / …-error-port-sink | Routes through a Scheme output port (or current-error-port) |
log:set-level! / log:set-global-level! | Per-logger or process-wide threshold (trace…off) |
log:set-pattern! | spdlog format string for the line layout |
log:set-formatter! | 'human (default) or 'json |
log:flush! / log:flush-on! | Manual flush, or auto-flush above a level |
log:shutdown! | Drain and dispose every registered logger at exit |
Reference:
log.md.
16. Time, Freeze & Finalizers
std.time exposes time:now-ms, time:monotonic-ms, time:sleep-ms,
time:utc-parts, time:format-iso8601-utc, time:elapsed-ms — see
time.md.
std.freeze provides two attributed-variable combinators that compose
with the logic engine:
| Form | Meaning |
|---|---|
(freeze v thunk) | Run thunk when logic var v becomes ground |
(dif x y) | Structural disequality; succeeds iff x and y cannot unify |
See freeze.md and finalizers.md
for object-lifetime hooks.
17. Logic Programming & Unification
Logic variables (logic-var), structural unification (==), and the
search combinators (findall, run1, succeeds?, naf) are first
class. Backtracking is implemented by a trail managed at the VM level —
exception handling and CLP propagation compose with it cleanly.
(import std.logic)
(define parent-db
'((tom bob) (tom liz) (bob ann) (bob pat) (pat jim)))
(defun parento (p c)
(map* (lambda (f) (lambda () (and (== p (car f)) (== c (cadr f)))))
parent-db))
(let ((c (logic-var)))
(findall (lambda () (deref-lvar c)) (parento 'tom c)))
;; => (bob liz)
Reference:
logic.md.
18. Constraint Logic Programming
Three CLP domains are bundled:
| Domain | Module | Reference |
|---|---|---|
| CLP(FD) | std.clp | clp.md |
| CLP(B) | std.clpb | clpb.md |
| CLP(R) | std.clpr | clpr.md |
(import std.clp)
(let ((vars (list (clp:var) (clp:var) (clp:var))))
(for-each (lambda (v) (clp:domain v 1 9)) vars)
(clp:all-different vars)
(clp:solve vars))
std.clpr exposes interval domains, linear and quadratic
minimise/maximise routines backed by the Fourier–Motzkin oracle. See
cookbook/numerics/portfolio-lp.eta for a
worked LP.
19. Fact Tables
std.db provides Datalog-style relations with defrel, assert!, and
tabled evaluation — see db.md. std.fact_table is a
columnar store with hash-indexed lookups for analytics workloads — see
fact-table.md and
cookbook/numerics/fact-table.eta.
20. Causal Inference
std.causal answers questions of the form “what happens to Y if we
intervene on X?” from a graph and (optionally) data. A graph is an
edge list; from there you can identify an estimand, estimate it from
observations, run sensitivity checks, learn structure, or render the
graph for a notebook.
- Graphs and d-separation — DAGs and ADMGs (with
<->for unobserved confounders), ancestors, c-components, Bayes-ball. - Identification — back-door, front-door, IV, generalised adjustment, and the ID / IDC algorithms for arbitrary do-queries.
- Mediation — natural and controlled direct/indirect effects (NDE, NIE, CDE).
- Transport & counterfactuals — selection diagrams, the sBD criterion, twin networks, ID*, effect of treatment on the treated.
- Estimation — g-formula, IPW, AIPW, TMLE, plus bootstrap confidence intervals and sensitivity diagnostics (E-value, Rosenbaum bounds).
- Structure learning — PC, FCI, GES, NOTEARS, with Fisher-z and χ² conditional-independence tests.
- Rendering — DOT, Mermaid, and LaTeX output for the same edge lists you query against.
A graph is just an edge list — -> is a directed edge, <-> an
unobserved-confounder bidirected edge:
(import std.causal std.causal.identify)
(define finance-dag
'((sector -> market-beta)
(sector -> stock-return)
(market-beta -> stock-return)))
(do:identify finance-dag 'stock-return 'market-beta)
;; => (adjust (sector) (P stock-return market-beta (sector)) (P (sector)))
(id '((x -> m) (m -> y) (x <-> y)) '(y) '(x))
;; => (sum (m) (prod (P (m x)) (sum (x*) (prod (P (y x* m)) (P (x*))))))
Once an estimand is identified, plug observational data into the
estimation backends — every estimator returns a scalar ATE, and
do:bootstrap-ci wraps any of them for percentile CIs:
(import std.causal.estimate)
(define obs '(((x . 0) (z . 0) (y . 0.0)) ((x . 1) (z . 0) (y . 2.1))
((x . 0) (z . 1) (y . 9.8)) ((x . 1) (z . 1) (y . 12.0))
;; … many more rows …
))
(do:ate-gformula obs 'y 'x '(z)) ;; plug-in regression
(do:ate-aipw obs 'y 'x '(z)) ;; doubly-robust
(do:bootstrap-ci (lambda (b) (do:ate-aipw b 'y 'x '(z))) obs 500 0.05)
Render any graph straight to Mermaid or DOT for notebooks and papers:
(import std.causal.render)
(dag:->mermaid finance-dag) ;; flowchart LR …
(dag:->dot finance-dag '((title . "Finance DAG") (rankdir . "LR")))
| Module | Scope |
|---|---|
std.causal | DAG utilities, do-calculus rules, back-door identification, plug-in adjustment |
std.causal.admg | ADMGs, bidirected edges, latent projection, c-components |
std.causal.identify | ID / IDC algorithms, hedge detection, estimand simplifier |
std.causal.adjustment | Generalised adjustment, front-door, IV |
std.causal.mediation | Natural / controlled direct & indirect effects |
std.causal.transport | Selection diagrams, sBD criterion, transport queries |
std.causal.counterfactual | Twin networks, ID* / IDC*, effect-of-treatment-on-treated |
std.causal.estimate | g-formula, IPW, AIPW, TMLE, bootstrap CIs, E-value, Rosenbaum bounds |
std.causal.learn | PC / FCI / GES / NOTEARS structure learning, Fisher-z & χ² CI tests |
std.causal.render | DOT, Mermaid, LaTeX renderers; define-dag macro |
Reference:
causal.md; counterfactual deep-divecausal-counterfactual.md; end-to-end examplecookbook/causal/causal_demo.eta.
21. Automatic Differentiation (AAD)
Reverse-mode AD with a tape recorded directly by the VM — no closure
allocation per arithmetic op. grad returns (value gradient-vector)
in a single backward sweep over the tape.
(import std.aad)
(grad (lambda (x y) (+ (* x y) (sin x))) '(2 3))
;; => (8.909... #(2.583... 2))
Helpers for AD-safe primitives (ad-abs, softplus, relu,
check-grad) and tape introspection live in std.aad.
Reference:
aad.md.
22. Statistics & Linear Algebra
std.stats provides descriptive statistics, OLS multi-regression, PCA,
and distribution functions, backed by Eigen for dense linear algebra.
The Eigen layer is currently exposed only through std.stats and
std.torch — there is no separate user-facing module.
Reference:
stats.md.
23. libtorch / Neural Networks
std.torch wraps libtorch tensors, autograd, the nn module suite,
optimisers, and (when built with CUDA) device transfer.
(import std.torch)
(define x (torch:tensor '((1.0 2.0) (3.0 4.0)) '(:requires-grad #t)))
(define y (torch:matmul x (torch:transpose x 0 1)))
(torch:backward (torch:sum y))
(torch:grad x)
Reference:
torch.md; tests:cookbook/tests/torch/.
24. Concurrency & Distribution
Eta’s actor model is built on nng: every actor owns a mailbox
socket; messages are arbitrary Eta values serialised by the runtime.
The same send! / recv! API works for in-process threads, OS
processes, and remote TCP peers.
| Primitive | Use |
|---|---|
(spawn module-path) | Fork a child process running the named module |
(spawn-thread thunk) | Run a closure in a fresh in-process VM thread |
(current-mailbox) | Child-side handle to the parent / spawner |
(send! sock v 'wait) | Block until message is sent |
(recv! sock 'wait) | Block until a message arrives |
(monitor sock) | Receive a (down …) message when the peer dies |
High-level patterns provided by std.net: worker-pool,
request-reply, survey, PUB/SUB. Supervision trees (one-for-one,
one-for-all) live in std.supervisor.
References:
message-passing.md,networking.md,network-message-passing.md,supervisor.md.
25. Finance Examples
| Example | Topic | Walkthrough |
|---|---|---|
european.eta | Black–Scholes Greeks via AAD | european.md |
sabr.eta | SABR vol surface, Hagan approximation | sabr.md |
xva.eta | CVA / FVA sensitivities via AAD | xva.md |
xva-wwr/ | Wrong-Way Risk via do-interventions | featured/xva-wwr.md |
portfolio.eta | Causal portfolio engine (full pipeline) | featured/portfolio.md |
portfolio-lp.eta | LP variant via std.clpr | CLP(R) |
fact-table.eta | Columnar fact tables | fact-table.md |
26. Tooling
| Topic | Reference |
|---|---|
| REPL | repl.md |
Compiler (etac) | compiler.md, bytecode-and-tools.md |
| Bytecode VM | bytecode-vm.md |
| LSP / DAP / VS Code | vscode.md, debugging.md |
| Jupyter kernel | jupyter.md |
Testing (std.test, eta_test) | testing.md |
Profiling (eta prof, std.prof) | profiling.md, std.prof |
27. Runtime Internals (overview)
NaN-boxing
All Eta values fit in a 64-bit double-NaN payload: fixnums and small
immediates are encoded directly; heap objects (pairs, vectors,
strings, closures, records, …) use tagged pointers. See
nanboxing.md.
Bytecode VM
A stack-based VM with explicit Call / TailCall, SetupCatch /
Throw, and unification opcodes. See bytecode-vm.md
and runtime.md.
Garbage collector
Mark-and-sweep over the VM heap with explicit GC roots from the value
stack, frame stack, intern table, and registered finalizer set. See
runtime.md, finalizers.md.
Optimisations
etac -O runs constant folding, dead-code elimination, peephole
opcode fusion, and known-call inlining. See
optimisations.md.
For the architectural overview, read architecture.md.
28. Examples Index
A curated walk through everything in cookbook/:
beginner programs, symbolic & logic, AAD & finance, concurrency, causal
& portfolio engines, plus the notebook collection.
Tour:
examples-tour.md.
29. Further Reading
architecture.md— pipeline overviewnext-steps.md— short-term work itemsrelease-notes.md— version history