Skip to main content

Value

Enum Value 

Source
pub enum Value {
Show 23 variants Nil, Void, 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, }, Bytevector { len: usize, data: ArenaIndex, }, String { len: usize, data: ArenaIndex, }, Native { id: usize, }, Ref(ArenaIndex), Usize(usize), Syntax { expr: ArenaIndex, context: ArenaIndex, }, ContFrame { cont_data: ArenaIndex, env: ArenaIndex, }, ErrorObject { message: ArenaIndex, irritants_and_type: ArenaIndex, }, Continuation { cont_chain: ArenaIndex, metadata: ArenaIndex, }, Port(PortId), Eof,
}
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)

§

Void

Void/Unspecified value (R7RS)

Returned by side-effect-only forms like define, set!, display, etc. The REPL should not print anything when this is returned.

§

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.

Fields

§

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 symbols
  • body_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.

Fields

§params: ArenaIndex
§body_env: ArenaIndex
§

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 syntax

Fields

§len: usize
§

Bytevector

Bytevector (R7RS §6.9) with inline length and data pointer

Bytevectors store exact integers in the range 0–255 as Number values contiguously in the arena, reusing the same layout as Array.

§Memory Layout

  • len: Number of bytes (inline)
  • data: Points directly to first Number value in arena
  • Empty bytevectors have len=0 and data == NIL

§Example

#u8(0 10 5)           ; Bytevector literal
(bytevector-length #u8(1 2 3))  ; => 3

Fields

§len: usize
§

String

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)    ; => #\h

Fields

§len: usize
§

Native

Native function with inline id

Native functions are registered at runtime and identified by their ID. The actual function pointer is stored in the evaluator’s NativeRegistry.

Fields

§

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.

§

Syntax

Syntax object for procedural macros (syntax-case)

A syntax object wraps an expression with lexical context information for hygienic macro expansion. This is the foundation for implementing R6RS-style syntax-case macros.

§Memory Layout

  • expr: ArenaIndex to the wrapped datum (the actual S-expression)
  • context: ArenaIndex to cons cell (marks . substitutions)
    • car: list of marks for tracking hygiene scopes
    • cdr: substitution environment for identifier resolution

This maintains the 2-index constraint per arena slot, matching Lambda’s layout.

§Example

(syntax-case stx ()
  ((keyword arg ...)
   (with-syntax ((name (generate-name)))
     #'(define name (lambda () arg ...)))))

§References

  • R6RS Chapter 11 (syntax-case)
  • “Macros that Work” (Clinger & Rees, 1991)
  • psyntax (Dybvig, Hieb, Bruggeman)

Fields

§context: ArenaIndex
§

ContFrame

Continuation frame for arena-based continuation stack (call/cc support)

Continuation frames form a linked list in the arena, enabling O(1) capture for call/cc. Each frame stores the continuation type, associated data, and a reference to the parent continuation.

§Memory Layout

  • cont_data: ArenaIndex to cons cell ((type . data) . parent_cont)
    • car: cons cell (Usize(cont_type) . data) where data encodes continuation-specific values
    • cdr: ArenaIndex to parent ContFrame, or Nil for Done
  • env: ArenaIndex to the environment at this continuation point

This maintains the 2-index constraint per arena slot, matching Lambda’s layout.

§Example Continuation Types (encoded as Usize)

  • 0: Done - computation complete
  • 1: ApplyForced - after evaluating function
  • 2: IfBranch - after evaluating condition
  • etc.

§References

See docs/CALL_CC_IMPLEMENTATION_PLAN.md for the full implementation plan.

Fields

§cont_data: ArenaIndex
§

ErrorObject

R7RS error object (§6.11)

Created by the error procedure. Stores the error message and associated irritant values for structured exception handling.

§Memory Layout

  • message: ArenaIndex to a Value::String or Value::Symbol containing the error message
  • irritants_and_type: ArenaIndex to a cons cell (irritants . error_type)
    • car: list of irritant values passed to error
    • cdr: error type (Nil for standard (error msg ...) calls)

§Example

(error "out of range" 42)       ; message="out of range", irritants=(42), type=()
(guard (e ((error-object? e) (error-object-message e)))
  (error "bad value" 1 2 3))    ; => "bad value"

Fields

§message: ArenaIndex
§irritants_and_type: ArenaIndex
§

Continuation

Captured continuation from call/cc - a first-class callable value

When call-with-current-continuation (call/cc) is invoked, the current continuation is captured and wrapped as this value type. The continuation can later be invoked as a procedure, which abandons the current computation and returns to the point where the continuation was captured.

§Memory Layout

Following the 2-index constraint like Lambda and Cons:

  • cont_chain: ArenaIndex to ContFrame linked list (captured continuation stack)
    • Points to the head of the continuation chain (most recent frame)
    • Nil represents an empty continuation (e.g., at top-level REPL with no pending work)
  • metadata: ArenaIndex to cons cell (capture_env . dynamic_wind_chain)
    • car: environment at capture point
    • cdr: dynamic-wind chain for proper before/after thunk invocation

§Semantics

When a continuation is called with a value:

  1. The current computation is abandoned
  2. The captured continuation stack is restored
  3. The value becomes the result at the call/cc point

§Example

(+ 1 (call/cc (lambda (k) (+ 2 (k 3)))))
;; => 4 (not 6, because (k 3) never returns)

§References

See docs/CALL_CC_IMPLEMENTATION_PLAN.md for the full implementation plan.

Fields

§cont_chain: ArenaIndex
§metadata: ArenaIndex
§

Port(PortId)

I/O Port (R7RS §6.13)

A first-class port value identified by a PortId. Standard ports (stdin=0, stdout=1, stderr=2) are predefined; additional ports can be opened for string or file I/O.

§

Eof

End-of-file object (R7RS §6.13)

A unique value returned by read operations when the end of input is reached.

Implementations§

Source§

impl Value

Source

pub const fn is_nil(&self) -> bool

Check if this value is nil (empty list)

Source

pub const fn is_void(&self) -> bool

Check if this value is void (unspecified value)

Void is returned by side-effect-only forms like define, set!, display. The REPL should not print anything when this value is returned.

Source

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

Source

pub const fn is_true(&self) -> bool

Check if this value is true (#t)

Source

pub const fn is_boolean(&self) -> bool

Check if this value is a boolean (#t or #f)

Source

pub const fn is_atom(&self) -> bool

Check if this value is an atom (not a cons cell)

Source

pub const fn is_number(&self) -> bool

Check if this value is a number

Source

pub const fn is_integer(&self) -> bool

Check if this value is an integer

Source

pub const fn as_ref(&self) -> Option<ArenaIndex>

Extract ArenaIndex from a Ref value. Returns None if not a Ref.

Source

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

Source

pub const fn is_symbol(&self) -> bool

Check if this value is a symbol

Source

pub const fn is_cons(&self) -> bool

Check if this value is a cons cell (pair)

Source

pub const fn is_lambda(&self) -> bool

Check if this value is a lambda

Source

pub const fn is_builtin(&self) -> bool

Check if this value is a builtin

Source

pub const fn is_stdlib(&self) -> bool

Check if this value is a stdlib function

Source

pub const fn is_native(&self) -> bool

Check if this value is a native (Rust) function

Source

pub const fn is_procedure(&self) -> bool

Check if this value is a procedure (lambda, builtin, stdlib, or native function)

Source

pub const fn is_array(&self) -> bool

Check if this value is an array

Source

pub const fn is_bytevector(&self) -> bool

Check if this value is a bytevector

Source

pub const fn is_string(&self) -> bool

Check if this value is a string

Source

pub const fn is_ref(&self) -> bool

Check if this value is a ref (internal arena index reference)

Source

pub const fn as_number(&self) -> Option<isize>

Get the number value if this is an integer

Source

pub const fn as_char(&self) -> Option<char>

Get the char value if this is a char

Source

pub const fn is_usize(&self) -> bool

Check if this value is a usize (internal unsigned integer)

Source

pub const fn as_usize(&self) -> Option<usize>

Get the usize value if this is a Usize

Source

pub const fn type_name(&self) -> &'static str

Get a human-readable type name

Source

pub const fn is_syntax(&self) -> bool

Check if this value is a syntax object

Source

pub const fn is_cont_frame(&self) -> bool

Check if this value is a continuation frame

Source

pub const fn is_continuation(&self) -> bool

Check if this value is a captured continuation (from call/cc)

Trait Implementations§

Source§

impl Clone for Value

Source§

fn clone(&self) -> Value

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Value

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Value

Source§

fn eq(&self, other: &Value) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

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>(&self, tracer: F)
where F: FnMut(ArenaIndex),

Trace all ArenaIndex references contained in this value. Read more
Source§

fn trace_with_arena<F>(&self, _arena: &Arena<Value, N>, tracer: F)
where F: FnMut(ArenaIndex),

Trace with arena access for types that store metadata in the arena. Read more
Source§

impl Copy for Value

Source§

impl StructuralPartialEq for Value

Auto Trait Implementations§

§

impl Freeze for Value

§

impl RefUnwindSafe for Value

§

impl Send for Value

§

impl Sync for Value

§

impl Unpin for Value

§

impl UnwindSafe for Value

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.