pub enum Value {
Show 16 variants
Nil,
True,
False,
Number(isize),
Char(char),
Cons {
car: ArenaIndex,
cdr: ArenaIndex,
},
Symbol(ArenaIndex),
Lambda {
params: ArenaIndex,
body_env: ArenaIndex,
},
Builtin(Builtin),
StdLib(StdLib),
Array {
len: usize,
data: ArenaIndex,
},
String {
len: usize,
data: ArenaIndex,
},
Native {
id: usize,
name_hash: usize,
},
Ref(ArenaIndex),
Usize(usize),
SyntaxRules {
literals: ArenaIndex,
rules_env: ArenaIndex,
},
}Expand description
A Lisp value
§Memory Optimization
This enum inlines fixed-size data directly into variants to save arena slots and improve cache locality. Variable-length data (strings, arrays) still uses arena storage but with inline length for O(1) access.
Variants§
Nil
The empty list (NOT false - use False for that)
True
Boolean true (#t)
False
Boolean false (#f) - the ONLY false value
Number(isize)
Integer number
Char(char)
Single character (used in strings and symbol storage)
Cons
Cons cell (pair) with inline car/cdr indices
Both car and cdr are stored inline, saving 2 arena slots per cons.
§Memory Savings
Previously: 1 slot for Cons + 2 slots for [Ref(car), Ref(cdr)] = 3 slots Now: 1 slot for Cons with inline car/cdr = 1 slot (saves 2 slots)
Use Lisp::cons(), Lisp::car(), Lisp::cdr() to create and access.
Symbol(ArenaIndex)
Symbol (contains a contiguous string) Points to a Value::String which contains the symbol name. The length is obtained from the String value, providing a single source of truth.
Lambda
Lambda / closure with inline params and body_env indices
params: ArenaIndex to list of parameter symbolsbody_env: ArenaIndex to a cons cell containing (body . env)
§Memory Savings
Previously: 1 slot for Lambda + 3 slots for [Ref(params), Ref(body), Ref(env)] = 4 slots Now: 1 slot for Lambda with inline params/body_env + 1 cons for body.env = 2 slots (saves 2 slots)
Use Lisp::lambda() to create and Lisp::lambda_parts() to extract.
Builtin(Builtin)
Built-in function (optimized)
StdLib(StdLib)
Standard library function (stored in static memory)
Unlike Lambda which stores code in the arena, StdLib references static function definitions. The function body is parsed on each call.
§Memory Efficiency
- Function definitions are in static memory (const strings)
- No arena allocation for the function definition itself
- Parsed AST is temporary and GC’d after evaluation
Array
Vector/Array with inline length and data pointer
Vectors store values contiguously in the arena. The length is inlined for O(1) access, saving 1 arena slot per array.
§Memory Layout
len: Number of elements (inline)data: Points directly to first element (no length header in arena)- Empty arrays have len=0 and data == NIL
§Memory Savings
Previously: 1 slot for Array + Number(len) header + elements Now: 1 slot for Array with inline len + elements only (saves 1 slot)
§Example
(define vec (make-vector 3 0)) ; Create vector of 3 zeros
(vector-set! vec 1 42) ; Set index 1 to 42
(vector-ref vec 1) ; => 42
(vector-length vec) ; => 3 (O(1) - inline!)
#(1 2 3) ; Vector literal syntaxString
String with inline length and data pointer
Strings store characters contiguously in the arena. The length is inlined for O(1) access, saving 1 arena slot per string.
§Memory Layout
len: Number of characters (inline)data: Points directly to first Char value (no length header in arena)- Empty strings have len=0 and data == NIL
§Memory Savings
Previously: 1 slot for String + Number(len) header + chars Now: 1 slot for String with inline len + chars only (saves 1 slot)
§Example
(string-length "hello") ; => 5 (O(1) - inline!)
(string-ref "hello" 0) ; => #\hNative
Native function with inline id and name_hash
Native functions are registered at runtime and identified by their ID. The actual function pointer is stored in the evaluator’s NativeRegistry.
§Memory Savings
Previously: 1 slot for Native + 2 slots for [Usize(id), Usize(name_hash)] = 3 slots Now: 1 slot for Native with inline id/name_hash = 1 slot (saves 2 slots)
Ref(ArenaIndex)
Raw arena index reference
Used internally for storing arena indices in contiguous blocks. This allows other variants (like Cons) to store their references in the arena rather than inline, enabling memory optimizations.
§Note
This is an internal implementation detail and should not be exposed to Lisp code directly. It’s traced by the GC like any other reference.
Usize(usize)
Unsigned integer (internal use)
Used for storing unsigned values like array lengths, indices, or other internal counters that need the full positive range of a machine word.
§Note
This is primarily an internal implementation detail. For user-facing
integers, prefer Number(isize) which supports negative values.
SyntaxRules
Syntax-rules macro transformer
Stores a compiled macro with literals, rules, and definition environment. Following the same 2-index constraint as Lambda, we pack fields into cons cells.
§Memory Layout
literals: ArenaIndex to list of literal keyword symbolsrules_env: ArenaIndex to cons cell (rules . definition_env)- car: list of (pattern . template) pairs
- cdr: environment where macro was defined (for hygiene)
This matches Lambda’s layout: 2 inline ArenaIndex fields = 1 arena slot.
§Example
(define-syntax when
(syntax-rules ()
((when test body ...)
(if test (begin body ...)))))Stored as:
- literals: ()
- rules_env: (rules_list . global_env) where rules_list = (((when test body …) . (if test (begin body …))))
Implementations§
Source§impl Value
impl Value
Sourcepub const fn is_false(&self) -> bool
pub const fn is_false(&self) -> bool
Check if this value is false (#f) This is the ONLY way to be false in this Lisp
Sourcepub const fn is_boolean(&self) -> bool
pub const fn is_boolean(&self) -> bool
Check if this value is a boolean (#t or #f)
Sourcepub const fn is_integer(&self) -> bool
pub const fn is_integer(&self) -> bool
Check if this value is an integer
Sourcepub const fn as_ref(&self) -> Option<ArenaIndex>
pub const fn as_ref(&self) -> Option<ArenaIndex>
Extract ArenaIndex from a Ref value. Returns None if not a Ref.
Sourcepub fn unwrap_ref(self) -> ArenaIndex
pub fn unwrap_ref(self) -> ArenaIndex
Extract ArenaIndex from a Ref value, panicking if not a Ref. Use only when you are certain the value is a Ref (e.g., after alloc_contiguous for Refs).
Sourcepub const fn is_builtin(&self) -> bool
pub const fn is_builtin(&self) -> bool
Check if this value is a builtin
Sourcepub const fn is_procedure(&self) -> bool
pub const fn is_procedure(&self) -> bool
Check if this value is a procedure (lambda, builtin, stdlib, or native function)
Sourcepub const fn is_ref(&self) -> bool
pub const fn is_ref(&self) -> bool
Check if this value is a ref (internal arena index reference)
Sourcepub const fn is_usize(&self) -> bool
pub const fn is_usize(&self) -> bool
Check if this value is a usize (internal unsigned integer)
Sourcepub const fn is_syntax_rules(&self) -> bool
pub const fn is_syntax_rules(&self) -> bool
Check if this value is a syntax-rules transformer
Trait Implementations§
Source§impl<const N: usize> Trace<Value, N> for Value
Implement Trace for GC support
impl<const N: usize> Trace<Value, N> for Value
Implement Trace for GC support
With inline fields, tracing is simpler - we just trace the ArenaIndex fields directly. No arena access needed to determine structure, which improves GC performance.
Source§fn trace<F: FnMut(ArenaIndex)>(&self, tracer: F)
fn trace<F: FnMut(ArenaIndex)>(&self, tracer: F)
ArenaIndex references contained in this value. Read more