Value model
| Eliscript | ECMAScript / Emacs Compat |
|---|---|
nil | null (only falsy value in eliscript-compat core) |
js-false / js-null / js-undefined | explicit high-fidelity JS tri-state sentinels |
t / false | true / false |
| numbers and strings | Emacs integer division (/ 7 2) => 3 in compat core; IEEE-754 in ESM |
| evaluated keywords | interned immutable Keyword values |
vector expressions & persistent-vector | 32-way persistent immutable Vectors (plus mutable '[] in compat core) |
list expressions & persistent-list | persistent linked Lists and Emacs cons cells |
map and set literals & persistent-map / persistent-set | persistent HAMT Maps and Sets (plus AVL persistent-sorted-map / persistent-sorted-set) |
#queue [...] / persistent-queue | persistent FIFO Queue |
cl-defstruct-persistent | immutable structural records with Schema validation & JSON Schema round-trip |
(js-array ...) / (js-object ...) | explicit native JavaScript containers |
In the 0.1.0 Emacs-compatible core (eliscript-compat), only nil is false—0, "", '[], and js-false are truthy, matching GNU Emacs Lisp 100%. In standalone .eli ESM modules, false, nil, and undefined are false while 0 and "" remain true.
Implemented forms & 1+5+2 extensions
defvar / defconst / defvar-onceTop-level dynamic/mutable, immutable, and hot-reload-preserving bindings.
defun / defn / defun-caseNamed functions and explicit multi-arity argument-count dispatch (defun-case / lambda-case).
defun-bind / let-bind / let*-bindUnified pcase-superset structural destructuring (:keys, :strs, :syms, :or, :as, &rest).
let-const / let*-constImmutable local bindings that reject setq / setf mutation at expansion time.
declare (eliscript-*)Zero-overhead eliscript-type, eliscript-pure, eliscript-pre, and eliscript-post contracts.
defportableWorker entries with a statically checked immutable dependency closure.
defmulti / defmethodNamed multimethods and source-ordered method definitions over the portable multimethod library.
defprotocol / eliscript-defprotocolOpen protocols with exact-type, host-category, and default implementations.
lambda / fn / letfnFirst-class anonymous and mutually recursive local functions.
let / let*Parallel and sequential lexical bindings (plus dynamic defvar binding in compat core).
if / when / unless / condValue-producing conditional forms.
pcase-value / cond-predicateUnified structural value pattern matching with (guard ...) and predicate dispatch with :>>.
if-let / when-let / if-some / when-someSingle-evaluation conditional bindings with truthy or non-nil matching.
case / condpSingle-evaluation constant or predicate dispatch with lazy selected results.
-> / ->> / as-> / cond-> / cond->>First-position, last-position, named, and conditional expression threading.
thread-some-> / thread-some->>Nil-short-circuiting pipelines that preserve js-false and falsy host values.
eliscript-loop / loop / recur50,000+ depth stack-safe iteration with checked tail positions and simultaneous rebinding.
defun-async / await / with-async-scopeAsync functions, structured task scopes with automatic sibling cancellation, timeouts, and bounded async-channel.
fn-comp / fn-partial / fn-juxt / fn-memoizeFunctional composition, partial application, juxtaposition, complement, and structural memoization.
condition-case / unwind-protect / catch / throwFull Emacs Lisp condition hierarchy (define-error) and non-local exit semantics.
7 Persistent Collectionspersistent-list, persistent-vector, persistent-map, persistent-set, persistent-queue, persistent-sorted-map, persistent-sorted-set.
cl-defstruct-persistentImmutable records with field :type Schema validation, copy-on-write -with updaters, and JSON Schema round-trip.
state-cell / state-swap!Observable atomic state containers with validator predicates and cycle-guarded watch notifications.
lazy-seq / xf-* / transduceMemoized lazy sequences and allocation-free composable Transducers (xf-map, xf-filter, xf-take).
transient / conj! / assoc! / persistent!Owner-isolated batch mutation builders with one-shot freeze enforcement.
[...] / {...} / #{...} / #queue [...]Persistent Vector, Map, Set, and Queue literals plus Emacs seq.el / map.el integration.
value-equal? / value-hashRecursive structural equality and deterministic hashing across all persistent collections.
js-obj / js-arr / js-get / js-set!Explicit JavaScript object and array construction and property mutation.
js-false / js-null / js-undefinedDistinct tri-state JS sentinels preserved losslessly by eliscript->js and js->eliscript.
js-call / js-new / js*Receiver-preserving method invocation, constructor calls, and explicit raw JS escape hatch.
with-eliscript-handleCross-engine opaque object handles with deterministic lifecycle revocation (eliscript-revoke-handle!).
:abi (:numeric-f64 / :memory-u32)Zero-glue typed numeric and linear-memory ABI lowered across Emacs, JS, WASM, and ARM64 Native.
mem-load-* / mem-store-*!Bounds-checked u32 and f64 linear-memory load/store operators.
Compile-time & checked macros
(defmacro-checked once (form)
`(let ((value$ ,form)) value$))
(defun load-once (value)
(once (+ value 1)))
defmacro and cl-defmacro definitions are processed in source order and removed before emission. Backquote, comma, ,@, &optional, &rest, and &body are supported. defmacro-checked enforces a pure compile-time sandbox that statically rejects side-effecting IO, process, network, or file mutations during macro expansion.
Both compiler generations interpret the same deterministic macro subset. Unlisted host functions, editor state, files, environment variables, and processes are unavailable unless a future explicit compiler capability exposes them.
ECMAScript modules & lexical defmodule
(import "eliscript/runtime/core/order" sort)
(import "react" :default React useState)
(import "library" :as Library)
(import "side-effect-only")
(export Component helper)
(export-default Component)
One Eliscript source file emits one ESM file. Alongside file-level (module ...), the eliscript-module extension provides (defmodule name (:export ...) (:import ...) ...) with lexical-private internal bindings (inaccessible externally while exposed to exported functions) and defvar-once hot-reload state preservation. Package subpath imports under eliscript/* support both extensionless specifiers (such as "eliscript/runtime/core/order" or "eliscript/runtime/emacs-compat") and explicit .mjs specifiers across Node, Bun, and the browser compiler. The repository itself tracks zero .mjs source files in Git; all JavaScript modules are synthesized from .eli sources during bootstrap.
Persistent collections
| Value | Implementation |
|---|---|
| List | Singly linked nodes with O(1) front operations and exact suffix sharing |
| Vector | 32-way bit-partitioned trie with a tail and O(log32 n) indexed updates |
| Map / Set | HAMT with bitmap, dense array, collision nodes, and path copying |
| Queue | Persistent front/rear Vectors with iterative million-value behavior |
| Sorted Map / Set | Structurally shared AVL trees with custom comparators and range queries |
All collection families participate in value equality, hashing, metadata, canonical data text, generic lookup, reduction, construction, stack, and reversible traversal protocols where applicable, with structural reference-identity (===) short-circuiting for shared subtrees and compact O(1)-step persistent Vector Zipper navigation across 12,000+ levels without prefix array copying. Records extend persistent Map behavior without surrendering their declared type identity.
Owner-token Transient Vector, Map, and Set builders support efficient isolated construction. persistent! completes the builder once; later mutation or reuse is rejected, and static compiler analysis prevents transient ownership from escaping trusted construction scopes.
Portable sequences
(import "../../stdlib/sequence.eli" map filter reduce range)
(map (lambda (value) (* value 2)) (range 1 5))
The standard library is written in Eliscript and includes persistent hash and AVL-sorted collections, protocols, replayable and memoized lazy sequences, pull transduction, immutable tree rewriting and zipper editing, text, numeric, Result, JSON, state, and host-interop modules without adding application-framework dependencies.
The maintained stdlib/core/seq.eli and runtime/core/sequence.eli modules are authored in Eliscript. They accept persistent, native, and externally extended reducible values through collection protocols, return persistent Vectors, add remove, and combine O(1) / O(log32 n) indexed fast-paths for first, last, and nth with unboxed early termination for find and circular array ring buffers with transient Vector builders for take-last and drop-last.
Portable text
(import "../../stdlib/text.eli" contains? strip-prefix trim)
(trim (strip-prefix "#" "# article "))
Thirteen functions provide literal matching, end-exclusive slicing, prefix and suffix removal, ASCII-boundary trimming, blank checks, joining, and repetition. They are written in portable Eliscript without regular expressions or host string methods. Indices follow ECMAScript UTF-16 code units rather than locale-aware grapheme clusters.
Immutable objects
(import "../../stdlib/object.eli" assoc pick)
(import "../../stdlib/data.eli" index-by)
(pick (assoc (object :name "Eliscript") :runtime "JavaScript")
[:name :runtime])
Eleven object functions enumerate own keys, test own properties, associate without mutation, remove and merge keys, transform values, and select fields. The data module adds keyed lookup, grouping, and counting by importing object primitives through explicit import-portable edges. Graph-aware portable builds verify those targets and prune every generated module.
The runtime-backed stdlib/core/data.eli and stdlib/core/order.eli surfaces return value-semantic persistent Maps and persistent Vector groups, add frequencies, sort, and sort-by, and use transient HAMT and Vector builders to eliminate intermediate persistent path copying.
Recursion and constant stack
(defun sum-to (n total)
(if (= n 0)
total
(recur (1- n) (+ total n))))
recur is checked at compile time and lowers self recurrence or a lexical loop to an iterative transfer with constant JavaScript stack use. The standard-library trampoline runs returned zero-argument thunks and supports open or mutual recursion with a fixed stack.
Ordinary calls in tail position remain ordinary JavaScript calls. General self and mutual tail-call optimization is recorded as Draft specification 0180 and receives no current implementation credit.
100% Emacs Lisp core & 1+5+2 architecture
- 100% Emacs Lisp default core (
eliscript-compat): Preserves Lisp-2 dual function/value namespaces,nil-only falsiness, lexical and dynamicdefvarscope, mutable native containers alongside 7 persistent collection types, integer division(/ 7 2) => 3,condition-case/define-error, and fulldefmacro/cl-defmacro/pcasecompatibility. - 7 explicit extension packs (
1+5+2): Activated by definition form without polluting standard Emacs Lisp —eliscript-contract,eliscript-data,eliscript-bind,eliscript-module,eliscript-async,eliscript-js, andeliscript-abi. - Four-target ABI execution: Functions annotated with
(declare (eliscript-abi :numeric-f64 ...))or(declare (eliscript-abi :memory-u32 ...))execute with identical semantics across Emacs Lisp, JavaScript ESM, WASM, and ARM64 Native binaries. - Standalone
.eliESM modules: Keep lexical Lisp-1 resolution, immutable persistent literals[...]/{...}/#{...}, and explicit ECMAScript module boundaries.
See Emacs-Compatible Syntax Specification (0.1.0) for the complete 1+5+2 specification and 50-case Emacs/JS Differential Oracle.