values

package
v1.19.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package values implements all Scheme runtime value types.

The package provides the complete R7RS value system:

Numeric Tower (R7RS 6.2.1)

All numeric types implement the Number interface for uniform arithmetic.

Core Types

I/O Ports (R7RS 6.13)

A single concrete type, PortObject, implements the Port marker interface and covers every port flavor. Capability-conditional surfaces are reached through its As*() (T, bool) accessors; the narrow flavors are named by the TypeInputPortTypeBinaryOutputPort ValueType constants.

Concurrency (SRFI-18+)

Singletons

Use Void for the absence of a value and EOFObject for end-of-file. EmptyList is the empty list sentinel.

Index

Constants

View Source
const (
	PositiveInfinityString = "+inf.0"
	NegativeInfinityString = "-inf.0"
	NaNString              = "+nan.0"
	NegativeNaNString      = "-nan.0"
)

External representations of the IEEE-754 special inexact reals, per R7RS §6.2.5. These are the exact lexemes the reader accepts and the writer emits for the infinities and NaN. Centralized here — the lowest package that both the reader (parser) and writer (Float/BigFloat SchemeString, number->string) depend on — so those sites share one source of truth rather than repeating the literals.

The writer only ever emits PositiveInfinityString / NegativeInfinityString / NaNString. NegativeNaNString exists solely because the reader accepts "-nan.0" as an input alias for NaN (there is no signed NaN in the external syntax); keep it paired with NaNString in reader case lists.

View Source
const (
	PrefixCharacter    = `#\`
	PrefixSyntax       = `#'`
	PrefixDirective    = `#!`
	PrefixBox          = `#&`
	PrefixPrimitive    = `#%`
	PrefixBlockComment = `#|`
	PrefixLineComment  = `;`

	SpecialEOF  = PrefixDirective + `eof`
	SpecialVoid = PrefixDirective + `void`
)

Prefix constants for Scheme value representations.

View Source
const DefaultBigFloatPrecision = 256

DefaultBigFloatPrecision is the default precision for BigFloat values.

View Source
const DefaultMaxWriteDepth int = 10000

DefaultMaxWriteDepth bounds structural nesting depth during writing.

The writer descends recursively into the car of each pair and into vector elements (the cdr-spine of a list is walked iteratively, so list *length* is unbounded — only nesting *depth* is capped). Without a bound, a deeply nested value — necessarily one built programmatically, since the reader caps textual input at parser.DefaultMaxParseDepth — overflows the host Go stack with a fatal, unrecoverable crash.

The default deliberately equals the parser's DefaultMaxParseDepth: the guiding invariant is "anything the writer emits must be valid on read." A value nested deeper than the reader accepts could not be read back, so the writer refuses it with ErrWriteDepthExceeded rather than emit unreadable output. The depth count matches readSyntax exactly (root = 1, +1 per container descent), so the write limit and the read limit trip on the same structures. 0 means unlimited. Mirrors the VM's DefaultMaxCallDepth, the parser's DefaultMaxParseDepth, and the expander's DefaultMaxExpandDepth.

View Source
const MaxCodepoint rune = 0x10FFFF

MaxCodepoint is the largest valid Unicode codepoint (U+10FFFF).

Variables

View Source
var (

	// FalseValue is the singleton false boolean.
	FalseValue = newBoolean(false)
	// TrueValue is the singleton true boolean.
	TrueValue = newBoolean(true)
)
View Source
var (
	SymbolMutexNotOwned  = NewSymbol("not-owned")
	SymbolMutexAbandoned = NewSymbol("abandoned")
)

Mutex state symbol singletons.

StateValue returns these instead of allocating fresh symbols on each call. The singletons avoid re-allocating the symbol; eq? on symbols is by name (see EqIdentity), so a singleton is eq? to a reader-produced 'not-owned.

View Source
var (

	// EmptyList is the singleton empty list ().
	// It implements Tuple but is not *Pair, enforcing (pair? '()) -> #f
	// at the type level per R7RS 6.4.
	//
	// It also satisfies SyntaxValue and SyntaxTuple — the empty list has
	// no symbols, scopes, or source-attachable hygiene content, so the
	// value-level singleton serves as the syntax-level singleton too
	// (matching Chez's `(equal? (syntax ()) '()) → #t`). For callers that
	// need the SyntaxTuple-typed view (e.g. so a SyntaxValue-returning
	// function can return the empty list directly), use SyntaxEmptyList
	// below — it refers to the same singleton.
	//
	// EmptyList is statically typed as Tuple (not SyntaxTuple) because the
	// common pattern `list := EmptyList; list = NewCons(...)` builds a
	// value-level list via type inference, and *Pair only implements
	// Tuple. Promoting EmptyList to SyntaxTuple would break that pattern.
	EmptyList Tuple = emptyListType{}

	// SyntaxEmptyList is the empty-list singleton typed as SyntaxTuple,
	// for use in contexts that build syntax-level lists or return
	// SyntaxValue. It is the same struct value as EmptyList; the public
	// package pkg/syntax re-exports this name.
	SyntaxEmptyList SyntaxTuple = emptyListType{}
)
View Source
var (
	SymbolAccuracyBelow = NewSymbol("below")
	SymbolAccuracyExact = NewSymbol("exact")
	SymbolAccuracyAbove = NewSymbol("above")
)

Accuracy singleton symbols — paraphrase big.Accuracy at the Scheme level. Returned by primitives like inexact-accuracy and inexact-with-accuracy.

'below — result < true value (rounded down) 'exact — result == true value (lossless) 'above — result > true value (rounded up)

View Source
var (
	SymbolThreadNew        = NewSymbol("new")
	SymbolThreadRunnable   = NewSymbol("runnable")
	SymbolThreadBlocked    = NewSymbol("blocked")
	SymbolThreadTerminated = NewSymbol("terminated")
	SymbolThreadUnknown    = NewSymbol("unknown")
	SymbolPrimordial       = NewSymbol("primordial")
)

Thread state symbol singletons.

StateSymbol and PrimCurrentThread return these package-level singletons instead of allocating fresh symbols on each call.

These are process-global pointers, but symbol identity in Wile is by name, not by pointer: EqIdentity compares *Symbol by .Key, so a singleton is eq? (and equal?) to a reader-produced symbol of the same name. The singletons exist only to avoid allocating a fresh *Symbol on every call. Observable via PrimCurrentThread, which yields SymbolPrimordial off a Thread: (eq? (current-thread) 'primordial) → #t.

The state symbols themselves have no Scheme-level primitive today: nothing exposes StateSymbol, so SRFI-18's thread-state is not reachable from Scheme.

View Source
var SyntaxValueUnwrapAllFunc func(SyntaxValue, map[SyntaxValue]Value) Value

SyntaxValueUnwrapAllFunc is the cycle-aware recursive unwrapper.

The full recursive unwrap traverses concrete syntax types (SyntaxPair, SyntaxObject, SyntaxSymbol, etc.) defined in pkg/syntax. Since values cannot import pkg/syntax (layering), the syntax package registers its UnwrapAllShared implementation here at init time.

MUST be non-nil before any SyntaxVector method that depends on it is called. pkg/syntax/syntax_vector.go init() sets this. A nil hook at call time indicates an init-order or import-graph violation; the methods that depend on it panic rather than silently degrade — silent degradation would corrupt hygiene or stack-overflow on cyclic data.

View Source
var SyntaxVectorAddScopeFunc func(*SyntaxVector, *Scope) SyntaxValue

SyntaxVectorAddScopeFunc implements recursive scope propagation across nested syntax types. The implementation lives in pkg/syntax (where the concrete syntax types are) and is registered here at init time.

MUST be non-nil. See SyntaxValueUnwrapAllFunc for the rationale.

Functions

func BigAcos

func BigAcos(x *big.Float, prec uint) *big.Float

BigAcos returns arccos(x) = π/2 − arcsin(x) rounded to prec bits, or nil when |x| > 1 (complex domain).

func BigAsin

func BigAsin(x *big.Float, prec uint) *big.Float

BigAsin returns arcsin(x) rounded to prec bits, or nil when |x| > 1 (complex domain — the caller falls back to the complex path).

func BigAtan

func BigAtan(x *big.Float, prec uint) *big.Float

BigAtan returns the arctangent of x rounded to prec bits.

func BigAtan2

func BigAtan2(y, x *big.Float, prec uint) *big.Float

BigAtan2 returns atan2(y, x) — the angle of the point (x, y) — rounded to prec bits.

func BigComplexAcos

func BigComplexAcos(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexAcos returns acos(z) = π/2 − asin(z).

func BigComplexAsin

func BigComplexAsin(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexAsin returns asin(z) = −i·ln(iz + √(1 − z²)).

func BigComplexAtan

func BigComplexAtan(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexAtan returns the complex arctangent atan(re + im·i) as (real, imag) parts rounded to prec bits, via atan(z) = (i/2)[ln(1 − iz) − ln(1 + iz)]. Computing at big precision keeps components beyond the float64 range from overflowing the way cmplx.Atan on a truncated complex128 would.

It agrees with math/cmplx.Atan on the whole plane except the branch cut along the imaginary axis for |Im z| > 1 with Re z = 0, where it returns the principal (Re > 0) value +π/2 rather than Go's signed-zero −π/2. Callers that need Go's convention on that cut should keep using cmplx.Atan in the float64 range (this function is reached only when a component overflows float64, where cmplx.Atan yields NaN and has no value to preserve).

func BigComplexCos

func BigComplexCos(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexCos returns cos(re + im·i) = cos(re)·cosh(im) − i·sin(re)·sinh(im).

func BigComplexExp

func BigComplexExp(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexExp returns exp(re + im·i) = exp(re)·(cos im + i·sin im).

func BigComplexLog

func BigComplexLog(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexLog returns log(re + im·i) = ½·ln(re²+im²) + i·atan2(im, re) — the principal branch (atan2 gives Arg ∈ [−π, π]; the −π endpoint is reached only for a negative-zero imaginary part, per IEEE atan2(−0, x<0) = −π).

func BigComplexSin

func BigComplexSin(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexSin returns sin(re + im·i) = sin(re)·cosh(im) + i·cos(re)·sinh(im).

func BigComplexTan

func BigComplexTan(re, im *big.Float, prec uint) (*big.Float, *big.Float)

BigComplexTan returns tan(z) = sin(z)/cos(z) by complex division.

func BigCos

func BigCos(x *big.Float, prec uint) *big.Float

BigCos returns cos(x) rounded to prec bits.

func BigE

func BigE(prec uint) *big.Float

BigE returns Euler's number e rounded to prec bits (= exp(1)), cached per precision; a defensive copy is returned so callers cannot mutate the cache.

func BigExp

func BigExp(x *big.Float, prec uint) *big.Float

BigExp returns eˣ rounded to prec bits. It range-reduces x = k·ln2 + r (k = round(x/ln2), |r| ≤ ln2/2), sums exp(r) by Taylor, and rescales by 2ᵏ via SetMantExp. Because 2ᵏ is a finite big.Float far below its exponent limit, this does not overflow the way math.Exp does past ~709 — exp(1000) is a finite big value. Astronomically large x (k beyond the big.Float exponent range) yields +Inf; astronomically negative x yields 0.

func BigIntegerEqualsFloat

func BigIntegerEqualsFloat(bi *BigInteger, f *Float) bool

BigIntegerEqualsFloat compares a BigInteger to a Float. Returns true only if the float exactly represents the BigInteger value.

func BigLog

func BigLog(x *big.Float, prec uint) *big.Float

BigLog returns the natural logarithm of x (x > 0) rounded to prec bits.

func BigPi

func BigPi(prec uint) *big.Float

BigPi returns π rounded to prec bits.

func BigSin

func BigSin(x *big.Float, prec uint) *big.Float

BigSin returns sin(x) rounded to prec bits.

func BigTan

func BigTan(x *big.Float, prec uint) *big.Float

BigTan returns tan(x) = sin(x)/cos(x) rounded to prec bits. At a pole (cos = 0) it returns +Inf.

func BooleanToBool

func BooleanToBool(b *Boolean) bool

BooleanToBool converts a Scheme *Boolean to a Go bool value.

func CarAs

func CarAs[T any](t Tuple, headSentinel error, name, role string) (T, error)

CarAs asserts t.Car() has concrete type T. Use this when the caller already holds a Tuple in hand and only needs a typed head — the tail is left implicit. For typed head + tail in one call, use UnconsTyped.

func DisplayValueToString

func DisplayValueToString(v Value) (string, error)

DisplayValueToString writes a Scheme value to a string for display with cycle detection. Unlike WriteValueToString, strings are printed without quotes and characters without #\. Returns ErrWriteDepthExceeded for values nested deeper than DefaultMaxWriteDepth.

func EqIdentity

func EqIdentity(a, b Value) bool

EqIdentity implements R7RS eq? semantics: pointer identity for all types except symbols, which compare by name/key (R7RS §6.1, §6.5). It is the single source of truth for eq?-identity — machine (the VM's promoted OpEqQ and continuation-mark scans) and registry/helpers both route through it. Living in values/ dissolves the machine↛registry import barrier that previously forced a duplicate copy. Kept small and branch-light so it inlines into the hot callers.

func Equal added in v1.19.0

func Equal(a, b Value) bool

Equal reports whether two values are structurally equal (R7RS equal?).

The traversal is iterative: containers decompose via DeepEqualer and their components are compared from a heap worklist, so no input can overflow the Go stack. Equal is total — it terminates on every value, cyclic or not, and has neither an error return nor a depth bound.

Push order is load-bearing. Pair pushes cdr before car, and the worklist pops last-in first, so a car's subtree drains before the traversal walks on down the spine. A flat list therefore holds one pending entry at a time rather than one per element.

func EqualTo

func EqualTo(a, b Value) bool

EqualTo compares two values for structural equality (R7RS equal?). It is the package-level spelling of Equal; see equal.go for the traversal.

func EqvNumber added in v1.19.0

func EqvNumber(a, b Number) bool

EqvNumber reports whether two numbers are eqv? per R7RS §6.1. It is the SINGLE authority on numeric equivalence in Wile.

Everything that asks "are these the same number?" routes here: eqv? (and so memv, assv, case) via registry/helpers.Eqv, and equal? (and so member, assoc, equal?-keyed hashtables) via each numeric type's EqualTo. That is not tidiness — §6.1 says equal? "returns the same as eqv? when applied to … numbers", with no latitude, so the two predicates MUST agree by construction rather than by two implementations happening to concur. They did not: the rule used to be written three times (helpers.Eqv, the per-type EqualTo methods, and the compiler's literalIdentical) and the copies disagreed on both signed zero and cross-representation inexacts.

The rules, each traceable to §6.1:

  • Identity first. Reflexivity is not optional, whatever the payload: eqv? settles identity before it looks at a value, and equal? may never be finer than eqv?. Without this a NaN would not be eqv? to itself, and (memv x lst) would fail to find the very object it was handed.

  • Exact vs inexact ⟹ #f. "one of obj1 and obj2 is an exact number but the other is an inexact number."

  • Both exact ⟹ compare numerically ACROSS representations. "both exact numbers and are numerically equal (in the sense of =)." An Integer 1 and a BigInteger 1 are the same number; how they are stored is not observable to a Scheme program, so representation must not be compared.

  • Both inexact ⟹ representation IS observable, so the kinds must match. A float64 and an arbitrary-precision BigFloat of equal value are NOT substitutable: (+ x 1e-20) tells them apart, which is exactly the "yield the same results … under any finite composition of Scheme's standard arithmetic procedures" test §6.1 states. This asymmetry with the exact case is the whole subtlety — same code shape, opposite verdicts, and exactness is what discriminates.

  • Signed zero is DISTINGUISHED. §6.1's note says (eqv? 0.0 -0.0) is #f "if negative zero is distinguished" — conditioned on the implementation, and Wile distinguishes it: (/ 1.0 -0.0) is -inf.0 while (/ 1.0 0.0) is +inf.0. That is a finite composition of standard arithmetic yielding different, non-NaN results, so the #f clause fires. Numeric comparison cannot see this (IEEE-754 says 0.0 == -0.0), which is why SignBit is consulted separately.

  • NaN ⟹ #t, but only against another NaN of the same kind. §6.1: "As an exception, the behavior of eqv? is unspecified when both obj1 and obj2 are NaN", so both answers conform; Wile follows Chez and Racket. Consult IsNaN, never IEEE `==`: eqv? is an equivalence relation and must be reflexive, which IEEE equality deliberately is not.

func ExactInteger

func ExactInteger(v Value) (int64, bool)

ExactInteger extracts an exact integer from a Scheme value. Returns the int64 value and true if the value is an exact integer that fits in int64. Returns 0 and false otherwise.

Accepts:

  • *Integer: direct int64 value
  • *BigInteger: if it fits in int64
  • *Rational: if denominator is 1 and numerator fits in int64

R7RS defines exact integers to include rationals like 2/1 that are mathematically integers. Call sites should check for non-negativity if required (e.g., for indexes).

func ForEachProperList

func ForEachProperList(ctx context.Context, t Tuple, name string, fn ForEachFunc) error

ForEachProperList calls fn on each element of t and returns ErrNotAList if the tail is not the empty list (i.e., t is an improper list). If fn returns an error, that error is returned unchanged, except that any error matching werr.ErrCircularList is rewrapped as ErrNotAList (with the cycle sentinel chained as its cause), since a circular list is an improper list.

This is the canonical proper-list eliminator — every site that walks a list and rejects improper tails should funnel through this function so the rejection logic is defined exactly once. registry/helpers.ForEachList delegates here; new code in any layer should call ForEachProperList directly when it cannot import the helpers package (e.g., machine/).

func FormatOriginChain

func FormatOriginChain(origin *OriginInfo, maxDepth int) string

FormatOriginChain returns a formatted string showing the macro expansion chain. maxDepth limits how many expansions to show (0 = unlimited).

func HasScope

func HasScope(scopes []*Scope, target *Scope) bool

HasScope checks if a scope set contains a specific scope

func IntegerEqualsFloat

func IntegerEqualsFloat(i *Integer, f *Float) bool

IntegerEqualsFloat compares an exact integer to an inexact float. Returns true only if the float exactly represents the integer value.

R7RS §6.2.5: Numeric equality must not lose precision. An exact integer and an inexact float are equal only if the float exactly represents the integer's value.

func IsEmptyList

func IsEmptyList(v Value) bool

IsEmptyList returns true if the value is the empty list. Returns false for nil values. For Tuple types, delegates to their IsEmptyList method.

func IsList

func IsList(v Value) bool

IsList returns true if the value is a proper list. A proper list is either EmptyList or a chain of pairs ending with EmptyList. Returns false for nil, improper lists (dotted pairs), and non-list values.

func IsVoid

func IsVoid(v Value) bool

IsVoid returns true if the value represents the absence of a meaningful result.

Void, EmptyList, and nil Semantics

The value system distinguishes three "absence/empty" concepts:

  • Void (voidType{} singleton): no meaningful result (e.g., set!, display). Canonical check: values.IsVoid(v) — handles both nil and the Void singleton.

  • EmptyList (emptyListType{} singleton): the empty list () — a valid first-class Scheme value. Implements Tuple but not *Pair. Canonical check: values.IsEmptyList(v) — handles nil safely (returns false).

  • Go nil (nil interface): uninitialized / absent in Go — not a Scheme value. IsVoid(nil) returns true; IsEmptyList(nil) returns false.

Anti-patterns to avoid:

  • v == values.EmptyList or v != values.EmptyList → use values.IsEmptyList(v)
  • v == values.Void → use values.IsVoid(v)
  • v == nil || values.IsVoid(v) → redundant; values.IsVoid(v) handles nil

Note: typed nil pointers (e.g., var p *Pair = nil) are handled by the type's IsVoid() method, which checks for nil receiver.

func Must

func Must(v Value, err error)

Must panics if err is non-nil or v is not EmptyList. Designed for use with ForEach on lists guaranteed to be proper:

Must(p.ForEach(ctx, func(...) error { ... }))

func NumberToComplex128Lossy

func NumberToComplex128Lossy(n Number) complex128

NumberToComplex128Lossy converts any Number to complex128, discarding per-component precision-loss signals. BigFloat and BigComplex values are reduced to float64/complex128 precision. Intended for paths where precision loss is acceptable, such as IEEE 754 Inf/NaN guards and inexact complex arithmetic in extensions. Callers needing loss signals should use ToComplex128WithAccuracy directly.

func NumberToFloat64

func NumberToFloat64(n Number) float64

NumberToFloat64 converts any Number to a best-effort float64 approximation.

Behavior across kinds:

  • Integer/BigInteger/Float/BigFloat/Rational: silent precision loss is possible (BigInteger > 2^53, BigFloat with extra precision, Rational like 1/3). Use ToFloat64WithAccuracy via the spec for loss signals.
  • Complex/BigComplex with imag == 0: returns the real part (lossless since no information is discarded).
  • Complex/BigComplex with imag != 0: panics with ErrNotAReal; the imaginary component cannot be carried in a float64. Callers in extensions/math should Simplify() the value first if they want zero-imag complex inputs to flow through transparently.

func NumericEquals

func NumericEquals(a, b Number) bool

NumericEquals implements R7RS = semantics for two numbers.

R7RS §6.2.5: The = procedure returns #t if its arguments are numerically equal. For IEEE 754 floats: infinities of the same sign are equal, NaN is not equal to anything (including itself). Cross-type Integer/BigInteger vs Float comparisons go through the exact-precision helpers above so no precision is lost; all other type pairs fall back to subtraction.

func SchemeTypeName

func SchemeTypeName(v Value) string

SchemeTypeName returns the Scheme-facing type name for a value. Used in error messages so users see "integer" instead of "*values.Integer".

Resolution proceeds in three layers:

  1. The goTypeToValueType reverse map covers concrete types backed by a ValueType constant — one lookup, no per-type case.
  2. A small explicit switch covers types whose Scheme name has no ValueType counterpart (records, boxes, promises).
  3. IsEmptyList catches the empty-list singleton.

Unrecognized types fall through to fmt.Sprintf("%T", v) as a debugging-grade name. Adding a new Value type with a ValueType constant means adding one row to goTypeToValueType, not editing this function.

func ScopeFingerprint added in v1.19.0

func ScopeFingerprint(scopes []*Scope) string

ScopeFingerprint builds a deterministic string from a scope set, so the set can key a map: two sets holding the same scopes (in any order) produce the same fingerprint, and any differing set produces a different one. Identity is by scope ID, matching ScopesMatch's pointer-identity model — scopes carry no structure to compare. The empty set fingerprints to "", and a non-empty set to sorted decimal IDs joined by ',', so the output contains only [0-9,].

func ScopesCompatible

func ScopesCompatible(bindingScopes, useScopes []*Scope) bool

ScopesCompatible checks whether a binding with bindingScopes can match a reference with useScopes. A binding with no scopes (top-level / pre-hygiene) matches any reference.

Both the environment's resolveLocal and the validator's duplicate-binding detection use this single function so scope resolution cannot diverge.

Note: nil useScopes does NOT mean "match any" here. A nil reference scope set means "this reference has no scopes" and behaves like an empty set — only bindings with no scopes match. Callers that want "match any" ask for it with AllScopes and short-circuit on ScopeSet.IsAll() before reaching this function (see EnvironmentFrame.resolveLocal).

func ScopesMatch

func ScopesMatch(useScopes, bindingScopes []*Scope) bool

ScopesMatch checks if two sets of scopes are compatible for binding resolution. This implements the core hygiene check using Flatt's "sets of scopes" model: A reference matches a binding if the binding's scope set is a SUBSET of the reference's scope set.

Powerset lattice P(S) (Flatt 2016, §3.2). Binding resolution is a subset test on finite scope sets.

match(ref, bind) ⟺ bind.scopes ⊆ ref.scopes
resolve(ref) = argmax { |s| : s ⊆ ref.scopes } over all bindings

where ref = useScopes, bind = bindingScopes,
s = a candidate binding's scope set, |s| = scope count.

Operations on P(S):
  AddScopeToSet    = join (union)
  RemoveScopeFromSet = relative complement
  FlipScopeInSet   = symmetric difference (XOR in Z/2Z^S)

Invariant: {} ⊆ X for all X — top-level bindings (empty scope set)
  match every reference. The argmax selects the most specific binding.
Constrains: GetLocalIndex (implements resolve/argmax),
  GetBinding (maximal resolution for scoped lookups),
  CompileSymbol (dispatches scoped vs unscoped lookup),
  scopesCompatibleForSubstitution (bidirectional subset = set equality).
Constrained by: NewScope (each macro invocation creates a fresh scope),
  FlipScopeInSet (syntax-local-introduce toggles scope membership).

See BIBLIOGRAPHY.md "Binding as Sets of Scopes".

This ensures: - Top-level bindings (empty scope set) match any reference: {} ⊆ X for all X - A macro-introduced binding only matches references with that macro's intro scope - User bindings don't capture macro-introduced identifiers (different scope sets)

Implementation note: Linear scan with pointer equality is intentionally used here. Scope sets are typically 0-4 elements (one per lexical form: macro invocation, lambda, let-syntax, with-binding-scope). For sets this small, linear scan is faster than hash-based or bitmap approaches due to cache locality and zero allocation overhead.

func Spine

func Spine(p *Pair, improperTail *Value) iter.Seq2[*Pair, struct{}]

Spine yields each *Pair along p's cdr chain. If the list terminates in EmptyList, *improperTail is set to EmptyList. If it terminates in a non-list cdr (improper list), *improperTail is set to that value. improperTail may be nil if the caller does not care about the tail.

Spine is the catamorphism for the initial list algebra

List = μX. 1 + Value × X

and is the irreducible spine-walk consumed by Pair.IsList, Length, AsVector, EqualTo, and SchemeString. It does NOT detect cycles — for cyclic input, use SpineWithCycleCheck.

The yielded value pair (*Pair, struct{}) uses struct{} so consumers can write either `for cell := range Spine(p, &tail)` (preferred) or `for cell, _ := range Spine(p, &tail)`.

func SpineWithCycleCheck

func SpineWithCycleCheck(p *Pair, cycled *bool) iter.Seq2[*Pair, struct{}]

SpineWithCycleCheck is Spine with Floyd's tortoise-and-hare cycle detection. *cycled is set to true if a cycle is detected, false otherwise. cycled may be nil if the caller does not care.

The iterator yields every cell up to (but not necessarily including) the point of cycle detection, and yields every cell of a proper or improper list before terminating. It does NOT report the improper tail — Floyd's algorithm cannot distinguish improper-tail termination from cycle detection in a single pass without an extra O(n) visited set. Callers that need both should use Spine with an external visited map.

func ToComplex128Lossless

func ToComplex128Lossless(n Number) (complex128, error)

ToComplex128Lossless returns the raw complex128, or ErrLossyConversion if either component's accuracy is non-Exact.

func ToFloat64Lossless

func ToFloat64Lossless(n Number) (float64, error)

ToFloat64Lossless is the FFI-strict convenience wrapper. Returns the raw float64 (callers in strict mode don't need the accuracy slot — they just want the value or an error). Returns ErrLossyConversion (wrapped, with direction info) if the conversion would lose precision OR drop the imaginary part.

func ToFloat64WithAccuracy

func ToFloat64WithAccuracy(n Number) (float64, big.Accuracy, bool, error)

ToFloat64WithAccuracy is the primary loss-signal-aware conversion helper. ToFloat64Lossless wraps it.

Returns 4-tuple positional:

  • f: the float64 representation, saturated to ±Inf for overflow per Go (*big.Float).Float64() semantics
  • acc: Below/Exact/Above per Go big.Accuracy semantics, describing *only the real-axis rounding direction* of the returned float64 against the original real-axis value: Below: f < real-axis true value (rounded down) Exact: f == real-axis true value (lossless) Above: f > real-axis true value (rounded up) For Complex/BigComplex with non-zero imag, acc describes the real component only; the imaginary-drop signal is carried solely by isReal. The two channels (acc, isReal) are orthogonal — never collapse a non-real input's loss into acc.
  • isReal: false iff n was Complex/BigComplex with non-zero imaginary part (the imaginary information is dropped — caller should use ToComplex128WithAccuracy for full complex semantics)
  • err: ErrNotANumber (wrapped) on a defensive nil-Number input. The signature is `n Number`, so a non-Number value cannot be passed — the nil case is the only reachable error path.

No information loss from the Go big package is introduced by this helper — every signal Go's stdlib surfaces is exposed through the four positional slots.

NaN/Inf contract (per design Q-6 resolution): NaN inputs return (NaN, Exact, true, nil) — NaN→NaN is bit-pattern identity in IEEE 754, so accuracy is Exact mechanically. A *true* infinite input (*Float(math.Inf(1))) returns (+Inf, Exact, true, nil). Finite values that overflow during conversion return (±Inf, Above|Below, true, nil). Callers checking "is this a meaningful real number?" must screen finiteness independently via math.IsNaN(f) and math.IsInf.

FFI lossy-allowed callers use this directly via the discard pattern `f, _, _, _ := ToFloat64WithAccuracy(n)`; FFI strict callers use ToFloat64Lossless.

func Uncons

func Uncons(v Value, name, role string) (Value, Value, error)

Uncons asserts v is a non-empty Tuple and projects (car, cdr). On empty list or non-Tuple input, returns a wrapped ErrNotAList with the canonical "<name>: <role>" message format. The cdr may be any Value — improper lists are accepted here; callers that need a proper-list tail should follow up with ForEachProperList.

Uncons is the eliminator for the Tuple algebra: every site that needs to peel one element off the front of a list and continue with the remainder should funnel through here so the empty-list / non-Tuple rejection is defined exactly once. registry/helpers.Uncons delegates here.

func ValidateByteValue

func ValidateByteValue(v *Integer, name string, desc string) error

ValidateByteValue checks that an integer is in the byte range [0, 255]. Returns a wrapped werr.ErrNotAByte error if the value is out of range.

func ValueToBool

func ValueToBool(b Value) bool

ValueToBool converts a value into a Go bool using Scheme semantics. In Scheme, only #f is false; everything else (including 0, "", '()) is true.

func WriteSharedValueToString

func WriteSharedValueToString(v Value) (string, error)

WriteSharedValueToString writes a Scheme value to a string with shared structure detection. Uses WriteModeWriteShared: datum labels for all multiply-referenced objects. R7RS §6.13.3: write-shared outputs datum labels for all shared structure. Returns ErrWriteDepthExceeded for values nested deeper than DefaultMaxWriteDepth.

func WriteValueToString

func WriteValueToString(v Value) (string, error)

WriteValueToString writes a Scheme value to a string with cycle detection. Uses WriteModeWrite: datum labels only for circular references. R7RS §6.13.3: write outputs datum labels only for objects that are part of a cycle. Returns ErrWriteDepthExceeded for values nested deeper than DefaultMaxWriteDepth.

Types

type AbandonedMutexException

type AbandonedMutexException struct {
	Mutex Value // always *Mutex; typed as Value so Scheme can carry it
}

AbandonedMutexException is raised when a mutex owner terminates

func (*AbandonedMutexException) Error

func (p *AbandonedMutexException) Error() string

type AtomicBox

type AtomicBox struct {
	// contains filtered or unexported fields
}

AtomicBox provides atomic operations on a Value.

func NewAtomicBox

func NewAtomicBox(initial Value) *AtomicBox

NewAtomicBox creates a new AtomicBox with the given initial value

func (*AtomicBox) CompareAndSwap

func (p *AtomicBox) CompareAndSwap(ol, nw Value) bool

CompareAndSwap atomically compares and swaps if current equals old. Returns true if the swap was performed.

Comparison is by identity of the stored Value, not by Scheme equal?: an equal-but-distinct object does not match. See atomicCell.

func (*AtomicBox) EqualTo

func (p *AtomicBox) EqualTo(v Value) bool

EqualTo returns true if the atomics are the same object.

func (*AtomicBox) ID

func (p *AtomicBox) ID() uint64

ID returns the AtomicBox's unique identifier

func (*AtomicBox) IsVoid

func (p *AtomicBox) IsVoid() bool

IsVoid returns true if the atomic is nil.

func (*AtomicBox) Load

func (p *AtomicBox) Load() Value

Load atomically loads and returns the value

func (*AtomicBox) SchemeString

func (p *AtomicBox) SchemeString() string

SchemeString returns the Scheme representation of the atomic.

func (*AtomicBox) Store

func (p *AtomicBox) Store(v Value)

Store atomically stores the value

func (*AtomicBox) Swap

func (p *AtomicBox) Swap(v Value) Value

Swap atomically stores new and returns the old value

type AtomicInt64

type AtomicInt64 struct {
	// contains filtered or unexported fields
}

AtomicInt64 provides atomic operations on int64 values This is more efficient than AtomicBox for integer operations

func NewAtomicInt64

func NewAtomicInt64(initial int64) *AtomicInt64

NewAtomicInt64 creates a new AtomicInt64 with the given initial value

func (*AtomicInt64) Add

func (p *AtomicInt64) Add(delta int64) int64

Add atomically adds delta and returns the new value

func (*AtomicInt64) CompareAndSwap

func (p *AtomicInt64) CompareAndSwap(ol, nw int64) bool

CompareAndSwap atomically compares and swaps Returns true if the swap was performed

func (*AtomicInt64) EqualTo

func (p *AtomicInt64) EqualTo(v Value) bool

EqualTo returns true if the atomics are the same object.

func (*AtomicInt64) ID

func (p *AtomicInt64) ID() uint64

ID returns the AtomicInt64's unique identifier

func (*AtomicInt64) IsVoid

func (p *AtomicInt64) IsVoid() bool

IsVoid returns true if the atomic int64 is nil.

func (*AtomicInt64) Load

func (p *AtomicInt64) Load() int64

Load atomically loads and returns the value

func (*AtomicInt64) SchemeString

func (p *AtomicInt64) SchemeString() string

SchemeString returns the Scheme representation of the atomic int64.

func (*AtomicInt64) Store

func (p *AtomicInt64) Store(v int64)

Store atomically stores the value

func (*AtomicInt64) Swap

func (p *AtomicInt64) Swap(nw int64) int64

Swap atomically stores new and returns the old value

type BigComplex

type BigComplex struct {
	// contains filtered or unexported fields
}

BigComplex represents an arbitrary-precision complex number. The real and imaginary parts can be *BigInteger, *Rational (exact), or *BigFloat (inexact).

R7RS §6.2.1: Complex numbers are part of the numeric tower hierarchy:

number ⊃ complex ⊃ real ⊃ rational ⊃ integer

R7RS §6.2.2: BigComplex is exact if both parts are BigInteger or Rational, inexact if either part is BigFloat. Operations follow exactness contagion rules.

func NewBigComplex

func NewBigComplex(rel, iam Number) *BigComplex

NewBigComplex creates a new BigComplex from real and imaginary parts. Parts must be *BigInteger, *Rational, or *BigFloat. Other types will panic.

func NewBigComplexFromBigFloats

func NewBigComplexFromBigFloats(rel, iam *BigFloat) *BigComplex

NewBigComplexFromBigFloats creates an inexact BigComplex from BigFloat parts.

func NewBigComplexFromBigIntegers

func NewBigComplexFromBigIntegers(rel, iam *BigInteger) *BigComplex

NewBigComplexFromBigIntegers creates an exact BigComplex from BigInteger parts.

func (*BigComplex) Abs

func (p *BigComplex) Abs() Number

Abs returns the magnitude of this BigComplex as a Number.

R7RS §6.2.6: For complex numbers, abs returns the magnitude.

func (*BigComplex) Add

func (p *BigComplex) Add(o Number) Number

Add returns the sum of this BigComplex and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: exact + exact = exact, exact + inexact = inexact.

func (*BigComplex) Conjugate

func (p *BigComplex) Conjugate() *BigComplex

Conjugate returns the complex conjugate (a-bi for a+bi).

func (*BigComplex) Divide

func (p *BigComplex) Divide(o Number) (Number, error)

Divide returns the quotient of this BigComplex and another number. Complex division: (a+bi)/(c+di) = ((ac+bd) + (bc-ad)i) / (c²+d²)

R7RS §6.2.6: The / procedure returns the quotient of its arguments. R7RS §6.2.2 Exactness: exact / exact = exact, exact / inexact = inexact.

func (*BigComplex) EqualTo

func (p *BigComplex) EqualTo(v Value) bool

EqualTo implements R7RS equal? for BigComplex.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*BigComplex) HashCode

func (p *BigComplex) HashCode() uint64

HashCode returns a hash of the complex value.

EQUALITY RECURSES INTO THE COMPONENTS, SO HASHING MUST TOO. EqvNumber decides a complex pair by recursing onto RealPart and ImagPart (see eqv.go), which means the Hashable contract — a.EqualTo(b) implies equal hashes — is discharged component-wise. Delegating to each component's own HashCode is therefore not merely tidy, it is the only way to inherit the component rules (NaN canonicalization, signed zero, the exact-family Integer/BigInteger/Rational collapse) instead of re-deriving them.

It used to hash toBigFloat(part).value directly. That bypassed BigFloat.HashCode, and so bypassed hashNaN: a NaN BigFloat carries an explicit nan flag with a ZERO backing big.Float, so a BigComplex with a NaN real part hashed identically to one with a 0.0 real part. Legal (a collision, and the contract is one-directional) but exactly the discipline hash.go had just been introduced to establish, and the one numeric HashCode not following it.

It also used to claim it matched Complex.HashCode "for cross-type consistency: when BigComplex.EqualTo(*Complex) holds". That relation cannot hold: EqvNumber separates inexact numbers by Kind, so a BigComplex is never eqv? to a Complex, and the branch's own TestBigComplex_EqualTo asserts as much.

func (*BigComplex) Imag

func (p *BigComplex) Imag() Number

Imag returns the imaginary part of the complex number.

func (*BigComplex) ImagAsBigFloat

func (p *BigComplex) ImagAsBigFloat() *BigFloat

ImagAsBigFloat returns the imaginary part converted to BigFloat for calculations.

The result ALIASES the component when it is already a *BigFloat: no copy is made. Callers that intend to mutate must copy: new(big.Float).Set(x.BigFloatValue()).

func (*BigComplex) ImagPart

func (p *BigComplex) ImagPart() Number

ImagPart returns the imaginary part of this complex number as a Number.

R7RS §6.2.6: imag-part returns the imaginary part of a complex number.

func (*BigComplex) IsExact

func (p *BigComplex) IsExact() bool

IsExact returns true if both parts are exact.

R7RS §6.2.2: A complex number is exact if both real and imaginary parts are exact.

This used to ask via isExactPart, a type switch on BigInteger|Rational that shadowed the parts' own IsExact(). The two agreed for every part type validateBigComplexPart admits, so it was not a bug -- but it was a second spelling of "is this exact", and it would have diverged silently the moment a part type was added. Ask the value, not its type.

func (*BigComplex) IsFinite

func (p *BigComplex) IsFinite() bool

IsFinite returns true if both real and imaginary parts are finite.

R7RS §6.2.6: finite? returns #t if neither part is Inf or NaN.

func (*BigComplex) IsInteger

func (p *BigComplex) IsInteger() bool

IsInteger returns true if the imaginary part is zero and the real part is an integer.

R7RS §6.2.6: integer? returns #t for complex numbers with zero imaginary part whose real part is an integer.

func (*BigComplex) IsNaN

func (p *BigComplex) IsNaN() bool

IsNaN returns true if either real or imaginary part is NaN.

R7RS §6.2.6: nan? returns #t for complex numbers with a NaN component.

func (*BigComplex) IsRational

func (p *BigComplex) IsRational() bool

IsRational returns true if this BigComplex is a real, finite number.

R7RS §6.2.6: rational? returns #t for finite real numbers. Inf and NaN are not rational, even when the imaginary part is zero.

func (*BigComplex) IsReal

func (p *BigComplex) IsReal() bool

IsReal reports whether this complex number is real.

R7RS §6.2: a complex is real iff its imaginary part is an *exact* zero — (real? 5+0i) => #t but (real? 5.0+0.0i) => #f. An inexact zero imaginary (a BigFloat 0.0) does not collapse to real. IsInteger/IsRational delegate here, so the whole integer? ⟹ rational? ⟹ real? hierarchy stays consistent.

func (*BigComplex) IsVoid

func (p *BigComplex) IsVoid() bool

IsVoid returns true if this BigComplex is nil.

func (*BigComplex) IsZero

func (p *BigComplex) IsZero() bool

IsZero returns true if both real and imaginary parts are zero.

func (*BigComplex) Kind

func (p *BigComplex) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*BigComplex) LessThan

func (p *BigComplex) LessThan(o Number) bool

LessThan compares the real parts of complex numbers. Following R7RS, < is not mathematically defined for complex numbers, but we follow the existing Complex.LessThan pattern of comparing real parts.

LessThan orders BigComplex values on their real parts only, matching Complex (complex.go). This is an internal total order, not mathematical ordering, and nothing may read it as such.

The NaN answer is owned by the real-part comparison, not by a guard here. A real-part NaN reaches BigFloat.LessThan (directly on the same-type path, or after promotion on the cross-kind path), which yields #f in both directions, exactly as Float gets #f free from IEEE <. An imaginary-part NaN is irrelevant to a real-parts-only ordering, so it is ignored, again matching Complex. An earlier guard on IsNaN() (real OR imag) over-reached: it made imag-NaN values unordered, a case Complex never treated specially.

func (*BigComplex) Magnitude

func (p *BigComplex) Magnitude() *BigFloat

Magnitude returns the absolute value (modulus) of the complex number. |a+bi| = sqrt(a² + b²)

func (*BigComplex) Multiply

func (p *BigComplex) Multiply(o Number) Number

Multiply returns the product of this BigComplex and another number. Complex multiplication: (a+bi)(c+di) = (ac-bd) + (ad+bc)i

R7RS §6.2.6: The * procedure returns the product of its arguments. R7RS §6.2.2 Exactness: exact * exact = exact, exact * inexact = inexact.

func (*BigComplex) Negate

func (p *BigComplex) Negate() Number

Negate returns the negation of this BigComplex.

func (*BigComplex) Phase

func (p *BigComplex) Phase() *BigFloat

Phase returns the phase (argument) of the complex number in radians. Uses atan2(imag, real) at big.Float precision, so components beyond the float64 range (~1.8e308) with a finite ratio keep their true angle instead of both saturating to +Inf and collapsing to atan2(+Inf,+Inf)=π/4.

func (*BigComplex) Real

func (p *BigComplex) Real() Number

Real returns the real part of the complex number.

func (*BigComplex) RealAsBigFloat

func (p *BigComplex) RealAsBigFloat() *BigFloat

RealAsBigFloat returns the real part converted to BigFloat for calculations.

The result ALIASES the component when it is already a *BigFloat: no copy is made. Callers that intend to mutate must copy: new(big.Float).Set(x.BigFloatValue()).

func (*BigComplex) RealPart

func (p *BigComplex) RealPart() Number

RealPart returns the real part of this complex number as a Number.

R7RS §6.2.6: real-part returns the real part of a complex number.

func (*BigComplex) SchemeString

func (p *BigComplex) SchemeString() string

SchemeString returns the Scheme representation of this BigComplex.

func (*BigComplex) Sqrt

func (p *BigComplex) Sqrt() *BigComplex

Sqrt returns the principal square root at big.Float precision, honoring the R7RS §6.2.6 branch cut along the negative real axis (continuous with quadrant II: the negative real axis maps to the positive imaginary axis). It uses the numerically stable formulation that derives the smaller component from the larger via division, avoiding catastrophic cancellation. Computing with big.Float instead of truncating to complex128 keeps components beyond the float64 range (~1.8e308) from overflowing the result to +inf.

func (*BigComplex) Subtract

func (p *BigComplex) Subtract(o Number) Number

Subtract returns the difference of this BigComplex and another number.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: exact - exact = exact, exact - inexact = inexact.

func (*BigComplex) ToExact

func (p *BigComplex) ToExact() (Number, error)

ToExact converts this BigComplex to an exact representation.

R7RS §6.2.6: exact returns an exact representation of its argument. If already exact, returns itself. Otherwise converts BigFloat parts exactly to *Rational (integer-valued results collapse to *BigInteger); the conversion is lossless. Returns the real part alone when the resulting imaginary part is zero. Non-finite parts return werr.ErrExactnessConversion.

func (*BigComplex) ToInexact

func (p *BigComplex) ToInexact() Number

ToInexact converts this BigComplex to an inexact representation.

R7RS §6.2.6: inexact returns an inexact representation of its argument. Only a wholly-exact BigComplex is converted: the guard is IsExact(), a conjunction over both parts, so a mixed-exactness BigComplex (exact real, inexact imag) is returned unchanged, converting nothing. When the converted imaginary part is zero, the real part alone is returned as a *BigFloat.

type BigFloat

type BigFloat struct {
	// contains filtered or unexported fields
}

BigFloat represents an arbitrary-precision floating-point number. Created with the #m prefix in Scheme (e.g., #m3.14159265358979323846).

big.Float natively supports ±Inf via SetInf/IsInf. NaN has no native big.Float representation (operations that produce NaN under IEEE 754 panic with big.ErrNaN instead), so NaN is tracked via an out-of-band flag.

Invariant: when nan is true, value MUST be a valid (zero-valued) *big.Float, never nil, to prevent nil-pointer panics.

func NewBigFloat

func NewBigFloat(v *big.Float) *BigFloat

NewBigFloat creates a new BigFloat from a big.Float.

func NewBigFloatFromFloat64

func NewBigFloatFromFloat64(v float64) *BigFloat

NewBigFloatFromFloat64 creates a new BigFloat from a float64.

func NewBigFloatFromString

func NewBigFloatFromString(s string) *BigFloat

NewBigFloatFromString creates a new BigFloat from a string. Returns nil if the string is not a valid number.

func NewBigFloatNaN

func NewBigFloatNaN() *BigFloat

NewBigFloatNaN creates a new BigFloat representing NaN.

func (*BigFloat) Abs

func (p *BigFloat) Abs() Number

Abs returns the absolute value of this BigFloat.

func (*BigFloat) Add

func (p *BigFloat) Add(o Number) Number

Add returns the sum of this BigFloat and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: inexact + inexact = inexact, exact + inexact = inexact.

func (*BigFloat) BigFloatValue

func (p *BigFloat) BigFloatValue() *big.Float

BigFloatValue returns the underlying big.Float value.

func (*BigFloat) Divide

func (p *BigFloat) Divide(o Number) (Number, error)

Divide returns the quotient of this BigFloat and another number.

func (*BigFloat) EqualTo

func (p *BigFloat) EqualTo(v Value) bool

EqualTo implements R7RS equal? for BigFloat.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*BigFloat) Float64Truncated

func (p *BigFloat) Float64Truncated() float64

Float64Truncated returns the value as float64, silently rounding when the magnitude exceeds float64 precision. Use only when downstream code inherently cannot use the accuracy bit (math.Sin/Cos inputs, FNV hash seeds, transcendental coercions).

For loss-signal-aware conversion, use Float64WithAccuracy() instead.

NaN handling: a BigFloat with the NaN flag set returns math.NaN().

func (*BigFloat) Float64WithAccuracy

func (p *BigFloat) Float64WithAccuracy() (float64, big.Accuracy)

Float64WithAccuracy returns the value as float64 along with Go's big.Accuracy indicator (Below / Exact / Above). Mirrors the stdlib (*big.Float).Float64() signature directly; the NaN flag is surfaced as (math.NaN(), big.Exact) since NaN→NaN is bit-pattern identity.

Use this whenever the caller can reasonably act on the accuracy bit (precision-aware conversions, the ToFloat64WithAccuracy public helper).

func (*BigFloat) HashCode

func (p *BigFloat) HashCode() uint64

HashCode returns a hash code for this BigFloat.

Every NaN hashes alike, via hashNaN: eqv? (and so equal?) identifies any two BigFloat NaNs, so the Hashable contract requires one hash for all payloads. ±Inf keep their bits: +inf.0 and -inf.0 are NOT eqv? and must be free to differ.

func (*BigFloat) IsExact

func (p *BigFloat) IsExact() bool

IsExact returns false since BigFloat is always inexact.

func (*BigFloat) IsFinite

func (p *BigFloat) IsFinite() bool

IsFinite returns true if this BigFloat holds a finite value.

R7RS §6.2.6: finite? returns #t for finite numbers.

func (*BigFloat) IsInteger

func (p *BigFloat) IsInteger() bool

IsInteger returns true if this BigFloat represents an integer value.

R7RS §6.2.6: integer? returns #t for inexact integers.

func (*BigFloat) IsNaN

func (p *BigFloat) IsNaN() bool

IsNaN returns true if this BigFloat holds NaN.

R7RS §6.2.6: nan? returns #t for NaN values.

func (*BigFloat) IsNegative

func (p *BigFloat) IsNegative() bool

IsNegative returns true if this BigFloat is negative. NaN has no sign and returns false.

func (*BigFloat) IsPositive

func (p *BigFloat) IsPositive() bool

IsPositive returns true if this BigFloat is positive. NaN has no sign and returns false.

func (*BigFloat) IsRational

func (p *BigFloat) IsRational() bool

IsRational returns true if this BigFloat holds a finite value.

R7RS §6.2.6: rational? returns #t for all finite real numbers. Inf and NaN are not rational.

func (*BigFloat) IsVoid

func (p *BigFloat) IsVoid() bool

IsVoid returns true if this BigFloat is nil.

func (*BigFloat) IsZero

func (p *BigFloat) IsZero() bool

IsZero returns true if this BigFloat is zero.

func (*BigFloat) Kind

func (p *BigFloat) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*BigFloat) LessThan

func (p *BigFloat) LessThan(o Number) bool

LessThan returns true if this BigFloat is less than another number.

func (*BigFloat) Multiply

func (p *BigFloat) Multiply(o Number) Number

Multiply returns the product of this BigFloat and another number.

func (*BigFloat) Negate

func (p *BigFloat) Negate() Number

Negate returns the negation of this BigFloat.

func (*BigFloat) SchemeString

func (p *BigFloat) SchemeString() string

SchemeString returns the Scheme representation of this BigFloat.

R7RS §6.2.6: Inexact integers must include a decimal point to distinguish them from exact integers. big.Float.Text('g', -1) drops ".0" for integer values, so we append it when neither '.' nor 'e'/'E' is present.

func (*BigFloat) Sign

func (p *BigFloat) Sign() int

Sign returns -1 if negative, 0 if zero, or 1 if positive. NaN returns 0 (NaN has no sign).

func (*BigFloat) SignBit added in v1.19.0

func (p *BigFloat) SignBit() bool

SignBit reports whether this big float carries a negative sign bit, INCLUDING -0.0.

big.Float.Sign() returns 0 for BOTH +0 and -0, so a negative zero is invisible to it -- the trap documented in Divide above. Signbit() reads the bit. NaN has no meaningful sign, so it reports false.

func (*BigFloat) Subtract

func (p *BigFloat) Subtract(o Number) Number

Subtract returns the difference of this BigFloat and another number.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: inexact - inexact = inexact, exact - inexact = inexact.

func (*BigFloat) ToExact

func (p *BigFloat) ToExact() (Number, error)

ToExact converts this BigFloat to an exact Rational.

R7RS §6.2.6: (exact +inf.0) and (exact +nan.0) are errors.

func (*BigFloat) ToInexact

func (p *BigFloat) ToInexact() Number

ToInexact returns this BigFloat unchanged since it's already inexact.

type BigInteger

type BigInteger struct {
	// contains filtered or unexported fields
}

BigInteger represents an arbitrary-precision integer. Created with the #z prefix in Scheme (e.g., #z12345678901234567890).

R7RS §6.2.1: Integers are exact numbers in the numeric tower hierarchy:

number ⊃ complex ⊃ real ⊃ rational ⊃ integer

R7RS §6.2.2: BigInteger is always exact. Operations on exact numbers produce exact results when mathematically well-defined.

R7RS §6.2.3: Implementations may support arbitrarily large exact integers. BigInteger provides this capability using Go's math/big.Int.

Precision Preservation

COMPARISON with a Float is lossless: comparisonTable (promotion.go) promotes both operands to BigFloat, so a BigInteger with more than 53 significant bits is not truncated into the Float's mantissa. Comparing 2^53+1 with 2^53.0 would otherwise report equality, both operands having landed on the same float64.

ARITHMETIC with a Float does NOT promote to BigFloat. It follows R7RS §6.2.2 exactness contagion to the inexact operand's own kind (promotionTable Zone 2), because sending exact × Float to BigFloat "to preserve precision" silently minted 256-bit bignums. A program that needs the extra precision must stay exact, or pass an explicit #m operand.

func NewBigInteger

func NewBigInteger(v *big.Int) *BigInteger

NewBigInteger creates a new BigInteger from a big.Int.

func NewBigIntegerFromInt64

func NewBigIntegerFromInt64(v int64) *BigInteger

NewBigIntegerFromInt64 creates a new BigInteger from an int64.

func NewBigIntegerFromString

func NewBigIntegerFromString(s string, base int) *BigInteger

NewBigIntegerFromString creates a new BigInteger from a string. Returns nil if the string is not a valid integer.

func (*BigInteger) Abs

func (p *BigInteger) Abs() Number

Abs returns the absolute value of this BigInteger.

func (*BigInteger) Add

func (p *BigInteger) Add(o Number) Number

Add returns the sum of this BigInteger and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: exact + exact = exact (BigInteger), exact + inexact = inexact (Float/Complex).

func (*BigInteger) BigInt

func (p *BigInteger) BigInt() *big.Int

BigInt returns p's storage without copying. The result aliases p, and a BigInteger is reachable from every Scheme binding it flowed to, so mutating the result (Set, Neg, an Add writing into it) retroactively changes the value of an exact integer Scheme treats as immutable — at every one of those bindings, not just the one the caller has in hand.

The accessor does not copy because it sits on the numeric hot path and nearly every caller only reads (Cmp, Sign, Text, IsInt64, SetInt); a defensive copy here would allocate for all of them to protect against the rare mutator. A caller that intends to mutate owns the copy: new(big.Int).Set(v.BigInt()).

The in-place scratch operations in numeric_scratch.go are the mutators this contract is aimed at. They are safe only on a big.Int the caller allocated; none may be pointed at a *BigInteger's storage obtained here.

func (*BigInteger) Divide

func (p *BigInteger) Divide(o Number) (Number, error)

Divide returns the quotient of this BigInteger and another number.

R7RS §6.2.6: The / procedure returns the quotient of its arguments. For exact arguments, / may return a non-integer (Rational) when the mathematical result is not an integer. Returns BigInteger only when the division is exact (remainder is zero).

R7RS §6.2.2 Exactness: exact / exact = exact (BigInteger or Rational), exact / inexact = inexact (Float or Complex).

func (*BigInteger) EqualTo

func (p *BigInteger) EqualTo(v Value) bool

EqualTo implements R7RS equal? for BigInteger.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*BigInteger) HashCode

func (p *BigInteger) HashCode() uint64

HashCode returns a hash of the big integer value. Uses the canonical exact-family hash so that Integer, BigInteger, and Rational produce identical hashes for equal values.

func (*BigInteger) Int64

func (p *BigInteger) Int64() int64

Int64 returns the value as int64 (may overflow for large values).

func (*BigInteger) IsExact

func (p *BigInteger) IsExact() bool

IsExact returns true as BigInteger is always exact.

R7RS §6.2.2: Integers (including BigInteger) are always exact.

func (*BigInteger) IsFinite

func (p *BigInteger) IsFinite() bool

IsFinite returns true since integers are always finite.

R7RS §6.2.6: finite? returns #t for all exact numbers.

func (*BigInteger) IsInteger

func (p *BigInteger) IsInteger() bool

IsInteger returns true since BigInteger is always an integer.

R7RS §6.2.6: integer? returns #t for exact integers.

func (*BigInteger) IsNaN

func (p *BigInteger) IsNaN() bool

IsNaN returns false since integers are never NaN.

R7RS §6.2.6: nan? returns #f for exact numbers.

func (*BigInteger) IsNegative

func (p *BigInteger) IsNegative() bool

IsNegative returns true if this BigInteger is negative.

func (*BigInteger) IsPositive

func (p *BigInteger) IsPositive() bool

IsPositive returns true if this BigInteger is positive.

func (*BigInteger) IsRational

func (p *BigInteger) IsRational() bool

IsRational returns true since integers are a subset of rationals.

R7RS §6.2.6: rational? returns #t for all real finite numbers.

func (*BigInteger) IsVoid

func (p *BigInteger) IsVoid() bool

IsVoid returns true if this BigInteger is nil.

func (*BigInteger) IsZero

func (p *BigInteger) IsZero() bool

IsZero returns true if this BigInteger is zero.

func (*BigInteger) Kind

func (p *BigInteger) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*BigInteger) LessThan

func (p *BigInteger) LessThan(o Number) bool

LessThan returns true if this BigInteger is less than another number.

R7RS §6.2.6: The < procedure returns #t if its arguments are monotonically increasing. Comparison across numeric types uses mathematical value.

func (*BigInteger) Multiply

func (p *BigInteger) Multiply(o Number) Number

Multiply returns the product of this BigInteger and another number.

R7RS §6.2.6: The * procedure returns the product of its arguments. R7RS §6.2.2 Exactness: exact * exact = exact, exact * inexact = inexact. Exception: Exact zero dominates—(* 0 x) may return exact 0 even when x is inexact. Zero is an exact value when the result is mathematically unambiguous. This implementation follows Chez Scheme's behavior.

func (*BigInteger) Negate

func (p *BigInteger) Negate() Number

Negate returns the negation of this BigInteger.

func (*BigInteger) SchemeString

func (p *BigInteger) SchemeString() string

SchemeString returns the Scheme representation of this BigInteger.

func (*BigInteger) Sign

func (p *BigInteger) Sign() int

Sign returns -1 if negative, 0 if zero, or 1 if positive.

func (*BigInteger) SignBit added in v1.19.0

func (p *BigInteger) SignBit() bool

SignBit reports whether this big integer carries a negative sign bit.

BigInteger is exact, so it has no signed zero and this coincides with IsNegative.

func (*BigInteger) Subtract

func (p *BigInteger) Subtract(o Number) Number

Subtract returns the difference of this BigInteger and another number.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: exact - exact = exact, exact - inexact = inexact.

func (*BigInteger) ToExact

func (p *BigInteger) ToExact() (Number, error)

ToExact returns this BigInteger as an exact number.

R7RS §6.2.6: exact returns an exact representation of its argument. Since BigInteger is already exact, it returns itself.

func (*BigInteger) ToInexact

func (p *BigInteger) ToInexact() Number

ToInexact returns this BigInteger converted to an inexact float.

R7RS §6.2.6: inexact returns an inexact representation of its argument. Converts to Float (float64), which may lose precision for large values.

R7RS §6.2.3: The inexact representation may have limited precision, but the conversion should be as close as practical.

PRECISION NOTE: For BigIntegers with more than 53 significant bits, precision is lost when converting to float64 (IEEE 754 binary64 has only 53 bits of mantissa precision). This is compliant with R7RS which allows inexact to be approximate.

type Boolean

type Boolean struct {
	Value bool
}

Boolean represents a Scheme boolean value.

func BoolToBoolean

func BoolToBoolean(b bool) *Boolean

BoolToBoolean converts a Go bool to a Scheme boolean value.

func ValueToBoolean

func ValueToBoolean(b Value) *Boolean

ValueToBoolean converts a value into a Scheme *Boolean using Scheme semantics.

func (*Boolean) EqualTo

func (p *Boolean) EqualTo(v Value) bool

EqualTo returns true if the values are equal booleans.

func (*Boolean) HashCode

func (p *Boolean) HashCode() uint64

HashCode returns a hash of the boolean value.

func (*Boolean) IsVoid

func (p *Boolean) IsVoid() bool

IsVoid returns true if the boolean is nil.

func (*Boolean) SchemeString

func (p *Boolean) SchemeString() string

SchemeString returns the Scheme representation of the boolean.

type Box

type Box struct {
	Value Value
}

Box represents a mutable Scheme box (container).

func NewBox

func NewBox(v Value) *Box

NewBox creates a new box containing the given value.

func (*Box) EqualComponents added in v1.19.0

func (p *Box) EqualComponents(v Value, push func(a, b Value)) bool

EqualComponents pushes the two boxes' contents for Equal to compare. A box is mutable, so a cycle can run through one; Equal's visited set closes it.

func (*Box) EqualTo

func (p *Box) EqualTo(v Value) bool

EqualTo returns true if the boxes contain equal values.

func (*Box) IsVoid

func (p *Box) IsVoid() bool

IsVoid returns true if the box is nil.

func (*Box) SchemeString

func (p *Box) SchemeString() string

SchemeString returns the Scheme representation of the box.

func (*Box) Unbox

func (p *Box) Unbox() Value

Unbox returns the boxed value.

type Byte

type Byte struct {
	Value uint8
}

Byte represents a Scheme byte value (0-255).

func NewByte

func NewByte(v uint8) *Byte

NewByte creates a new byte value.

func (*Byte) EqualTo

func (p *Byte) EqualTo(v Value) bool

EqualTo returns true if the bytes have equal values.

func (*Byte) HashCode

func (p *Byte) HashCode() uint64

HashCode returns a hash of the byte value.

func (*Byte) IsVoid

func (p *Byte) IsVoid() bool

IsVoid returns true if the byte is nil.

func (*Byte) SchemeString

func (p *Byte) SchemeString() string

SchemeString returns the Scheme representation of the byte.

type ByteUnreader

type ByteUnreader interface {
	UnreadByte() error
}

ByteUnreader is the interface satisfied by readers that can unread the last byte. Mirrors io.ByteScanner's UnreadByte half.

type ByteVector

type ByteVector []*Byte

ByteVector represents a Scheme bytevector.

func NewByteVector

func NewByteVector(vs ...*Byte) *ByteVector

NewByteVector creates a new bytevector from byte values.

func NewByteVectorFromBytes

func NewByteVectorFromBytes(vs ...byte) *ByteVector

func NewByteVectorFromIntegers

func NewByteVectorFromIntegers(vs ...*Integer) (*ByteVector, error)

NewByteVectorFromIntegers creates a new bytevector from integer values. Each integer must be in the range [0, 255] per R7RS §6.9.

func (*ByteVector) AsBytes

func (p *ByteVector) AsBytes(is ...int) []byte

AsBytes converts the bytevector to a Go byte slice. The starti and endi parameters specify the range of bytes to include. If starti is negative, it is treated as 0. If endi is greater than the length of the bytevector or negative, it is treated as the length of the bytevector. If starti is greater than endi, it is treated as equal to endi.

func (*ByteVector) AsList

func (p *ByteVector) AsList() Tuple

AsList converts the vector to a proper list (linked list of pairs). Returns void (nil Pair) if the vector is void. Returns EmptyList if the vector is empty. Otherwise returns a newly constructed list containing the vector's elements.

func (*ByteVector) EqualTo

func (p *ByteVector) EqualTo(v Value) bool

EqualTo returns true if the bytevectors have equal contents.

func (*ByteVector) Get

func (p *ByteVector) Get(i int) Value

func (*ByteVector) IsVoid

func (p *ByteVector) IsVoid() bool

IsVoid returns true if the bytevector is nil.

func (*ByteVector) Length

func (p *ByteVector) Length() int

func (*ByteVector) SchemeString

func (p *ByteVector) SchemeString() string

SchemeString returns the Scheme representation of the bytevector. Bytevector elements are bytes, never compound, so no cycle-detection set is needed (nil visited) and the depth bound is never reached (bytes cannot recurse). Elements sit one level below the bytevector root (depth 2).

func (*ByteVector) Set

func (p *ByteVector) Set(i int, value Value) error

Set sets the element at the specified index to the given value. ByteVectors are always mutable, so this never returns an error from immutability. Returns an error if the value is not a Byte.

type ByteVectorExtractor

type ByteVectorExtractor interface {
	ReadByteVector() (*ByteVector, error)
}

ByteVectorExtractor represents a port that can extract its accumulated bytes. Returned by (*PortObject).AsByteVectorExtractor.

type Callable

type Callable interface {
	Value
	AcceptsArity(n int) bool
}

Callable represents a Scheme procedure — any value that can be applied to arguments.

R7RS §6.1: The procedure? predicate returns #t for all callable types. This includes lambdas, case-lambdas, parameter objects (R7RS §4.2.6), and composable continuations.

AcceptsArity reports whether the procedure can be called with n arguments. This captures arity constraints that are otherwise scattered across per-type checks in the VM apply path. Fixed-arity callables (lambdas) accept only their declared count; variadic and continuation callables accept a range or any number — continuations resume with whatever number of values they are invoked with (R7RS §6.10), so they accept any arity.

type CharSet

type CharSet struct {
	// contains filtered or unexported fields
}

CharSet is an immutable set of Unicode codepoints stored as a sorted inversion list of disjoint, non-adjacent ranges. SRFI-14 char-set type.

Canonical form invariants (enforced by every constructor):

  1. Sorted: ranges[i].Lo > ranges[i-1].Hi
  2. Disjoint and non-adjacent: ranges[i].Lo > ranges[i-1].Hi + 1
  3. Non-empty: Lo <= Hi
  4. Codepoint-valid: 0 <= Lo, Hi <= MaxCodepoint

func NewCharSetFromRanges

func NewCharSetFromRanges(rs []CharSetRange) *CharSet

NewCharSetFromRanges constructs a CharSet from an already-canonical range slice. The caller asserts the slice is sorted, disjoint, non-adjacent, and codepoint-valid. Used internally by primitives that produce canonical output (set-algebra ops). External callers should prefer NewCharSetFromUnsortedRanges.

Panics on invariant violation — this is an internal contract assertion, wrapped per CLAUDE.md "NEVER panic with raw errors" imperative.

func NewCharSetFromRunes

func NewCharSetFromRunes(runes []rune) *CharSet

NewCharSetFromRunes builds a CharSet from a slice of codepoints (no canonicalization assumption). Each rune becomes a unit range, then canonicalized via NewCharSetFromUnsortedRanges.

func NewCharSetFromUnsortedRanges

func NewCharSetFromUnsortedRanges(rs []CharSetRange) *CharSet

NewCharSetFromUnsortedRanges constructs a CharSet from arbitrary range input. Invalid ranges (Lo > Hi or out-of-bounds) are dropped. Overlapping and adjacent ranges are merged. Result is in canonical form.

func (*CharSet) All

func (p *CharSet) All() iter.Seq[CharSetRange]

All returns an iter.Seq that yields each canonical range in codepoint ascending order. Caller breaks the loop with `break` to early-exit.

Cost: one closure allocation per accessor call (the iterator captures p). No O(n) slice copy — yields directly from the internal slice, which is safe because *CharSet is immutable. Strictly cheaper than Ranges() for any non-empty CharSet.

Naming follows Go stdlib convention (slices.All, maps.All).

func (*CharSet) Codepoints

func (p *CharSet) Codepoints() iter.Seq[rune]

Codepoints returns an iter.Seq that yields every codepoint in the set, in codepoint ascending order. Caller breaks the loop with `break` to early-exit.

func (*CharSet) Contains

func (p *CharSet) Contains(ch rune) bool

Contains reports whether the given codepoint is in the set, via binary search over the inversion list.

func (*CharSet) EqualTo

func (p *CharSet) EqualTo(v Value) bool

EqualTo implements Value (R7RS §6.1 equal?).

Two char-sets are equal iff their canonical range slices are equal — the invariants of canonical form make logical equality and structural equality the same thing.

func (*CharSet) IsVoid

func (p *CharSet) IsVoid() bool

IsVoid implements Value.

func (*CharSet) Ranges

func (p *CharSet) Ranges() []CharSetRange

Ranges returns a copy of the canonical range slice. Caller may mutate the returned slice without affecting the CharSet.

Most read-only iteration callers should prefer All or Codepoints — those avoid the O(n) defensive slice copy this method performs (they allocate a single iterator closure instead, which is cheaper for any non-empty CharSet). Ranges is retained for callers that genuinely need a slice: dual-cursor merge algorithms (intersect, difference) and the union builder that uses append on the result.

func (*CharSet) SchemeString

func (p *CharSet) SchemeString() string

SchemeString implements Value (R7RS §6.13.3 write).

func (*CharSet) Size

func (p *CharSet) Size() int

Size returns the total number of codepoints in the set.

type CharSetRange

type CharSetRange struct {
	Lo, Hi rune
}

CharSetRange is an inclusive-endpoint codepoint range.

type Character

type Character struct {
	Value rune
}

Character represents a Scheme character value.

func NewCharacter

func NewCharacter(v rune) *Character

NewCharacter creates a new character from a rune.

func (*Character) EqualTo

func (p *Character) EqualTo(v Value) bool

EqualTo returns true if both characters have the same rune value.

func (*Character) HashCode

func (p *Character) HashCode() uint64

HashCode returns a hash of the character value.

func (*Character) IsVoid

func (p *Character) IsVoid() bool

IsVoid returns true if the character is nil.

func (*Character) SchemeString

func (p *Character) SchemeString() string

SchemeString returns the Scheme representation of the character. Named characters use the R7RS mnemonic form (#\newline). Graphic characters use #\<char>. Non-graphic non-named characters use #\xHEX for round-trip safety.

func (*Character) String

func (p *Character) String() string

type CompileTimeValue

type CompileTimeValue struct {
	Value Value
}

CompileTimeValue wraps a value that is stored in the expand phase but accessible during macro expansion. This enables compile-time computation via define-for-syntax and begin-for-syntax.

func NewCompileTimeValue

func NewCompileTimeValue(v Value) *CompileTimeValue

NewCompileTimeValue creates a new compile-time value.

func (*CompileTimeValue) EqualComponents added in v1.19.0

func (p *CompileTimeValue) EqualComponents(v Value, push func(a, b Value)) bool

EqualComponents pushes the two wrapped values for Equal to compare.

func (*CompileTimeValue) EqualTo

func (p *CompileTimeValue) EqualTo(v Value) bool

EqualTo returns true if the compile-time values are equal.

func (*CompileTimeValue) IsVoid

func (p *CompileTimeValue) IsVoid() bool

IsVoid returns true if the compile-time value is nil.

func (*CompileTimeValue) SchemeString

func (p *CompileTimeValue) SchemeString() string

SchemeString returns the Scheme representation of the compile-time value.

func (*CompileTimeValue) Unwrap

func (p *CompileTimeValue) Unwrap() Value

Unwrap returns the underlying value.

type Complex

type Complex struct {
	Value complex128
}

Complex represents a Scheme complex number.

func NewComplex

func NewComplex(v complex128) *Complex

NewComplex creates a new complex number from a complex128 value.

func NewComplexFromParts

func NewComplexFromParts(realPart, imagPart float64) *Complex

NewComplexFromParts creates a new complex number from real and imaginary parts.

func (*Complex) Abs

func (p *Complex) Abs() Number

Abs returns the magnitude of this complex number.

R7RS §6.2.6: For complex numbers, abs returns the magnitude.

func (*Complex) Add

func (p *Complex) Add(o Number) Number

Add returns the sum of this complex number and another number. Zero short-circuit: 0+x=x preserves exactness per R7RS §6.2.2.

func (*Complex) Divide

func (p *Complex) Divide(o Number) (Number, error)

Divide returns the quotient of this complex number and another number.

func (*Complex) EqualTo

func (p *Complex) EqualTo(v Value) bool

EqualTo implements R7RS equal? for Complex.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*Complex) HashCode

func (p *Complex) HashCode() uint64

HashCode returns a hash of the complex value. Hashes real and imaginary parts independently via hashComplexComponent and combines them with a multiplicative mixing constant.

A NaN component is CANONICALIZED (hashNaN), not hashed bitwise: eqv? identifies every NaN, so the Hashable contract requires every NaN to hash alike, and the raw bits of (/ 0.0 0.0) and a literal +nan.0 differ. ±Inf components stay bit-exact — +inf.0 and -inf.0 are not eqv?, so they are free to hash differently and should.

func (*Complex) Imag

func (p *Complex) Imag() float64

Imag returns the imaginary part of the complex number.

func (*Complex) ImagPart

func (p *Complex) ImagPart() Number

ImagPart returns the imaginary part of this complex number as a Number.

R7RS §6.2.6: imag-part returns the imaginary part of a complex number.

func (*Complex) IsExact

func (p *Complex) IsExact() bool

IsExact returns false since Complex is always inexact.

R7RS §6.2.2: Complex numbers with floating-point components are inexact.

func (*Complex) IsFinite

func (p *Complex) IsFinite() bool

IsFinite returns true if both real and imaginary parts are finite.

R7RS §6.2.6: finite? returns #t if neither part is Inf or NaN.

func (*Complex) IsInteger

func (*Complex) IsInteger() bool

IsInteger reports whether this complex number is an integer.

R7RS §6.2: the predicate hierarchy is integer? ⟹ rational? ⟹ real? ⟹ complex?. A *Complex always has inexact (float64) components, so even a 0.0 imaginary part is an *inexact* zero — the value is not real (see IsReal), and therefore not rational or integer. (integer? 5.0+0.0i) => #f (Chez/Racket agree). Always false; integer-valued reals are represented by *Float/*Integer.

func (*Complex) IsNaN

func (p *Complex) IsNaN() bool

IsNaN returns true if either the real or imaginary part is NaN.

R7RS §6.2.6: nan? returns #t if any component is NaN.

func (*Complex) IsRational

func (*Complex) IsRational() bool

IsRational reports whether this complex number is rational.

R7RS §6.2: rational? ⟹ real?. A *Complex always has inexact components, so its zero imaginary part is an inexact zero and the value is not real, hence not rational. (rational? 5.0+0.0i) => #f. Always false.

func (*Complex) IsReal

func (*Complex) IsReal() bool

IsReal reports whether this complex number is real.

R7RS §6.2: a complex with an *inexact* zero imaginary part is NOT real — (real? 5.0+0.0i) => #f, while (real? 5+0i) => #t. A *Complex always stores inexact (float64) components, so its imaginary part (even 0.0) is an inexact zero. Exact-zero-imaginary complexes are represented by *BigComplex, never *Complex, so this is always false. (Chez/Racket agree.)

func (*Complex) IsVoid

func (p *Complex) IsVoid() bool

IsVoid returns true if this complex number is nil.

func (*Complex) IsZero

func (p *Complex) IsZero() bool

IsZero returns true if this complex number is zero.

func (*Complex) Kind

func (p *Complex) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*Complex) LessThan

func (p *Complex) LessThan(o Number) bool

LessThan compares the real parts of the complex numbers.

func (*Complex) Magnitude

func (p *Complex) Magnitude() float64

Magnitude returns the absolute value (modulus) of the complex number.

func (*Complex) Multiply

func (p *Complex) Multiply(o Number) Number

Multiply returns the product of this complex number and another number.

func (*Complex) Negate

func (p *Complex) Negate() Number

Negate returns the negation of this complex number.

R7RS §6.2.6: The - procedure with one argument returns the additive inverse.

func (*Complex) Phase

func (p *Complex) Phase() float64

Phase returns the phase (argument) of the complex number in radians.

func (*Complex) Real

func (p *Complex) Real() float64

Real returns the real part of the complex number.

func (*Complex) RealPart

func (p *Complex) RealPart() Number

RealPart returns the real part of this complex number as a Number.

R7RS §6.2.6: real-part returns the real part of a complex number.

func (*Complex) SchemeString

func (p *Complex) SchemeString() string

SchemeString returns the Scheme representation of this complex number. R7RS §6.2.6: Ensures decimal point for inexact values, lowercase inf/nan.

func (*Complex) Subtract

func (p *Complex) Subtract(o Number) Number

Subtract returns the difference of this complex number and another number.

func (*Complex) ToExact

func (p *Complex) ToExact() (Number, error)

ToExact converts this Complex to an exact representation.

R7RS §6.2.6: exact returns an exact representation of its argument. Both real and imaginary parts are converted to exact numbers.

The result goes through maybeSimplify, because converting the parts to exact is precisely what can make the demotion rule apply: an inexact 0.0 imaginary part becomes an EXACT zero, and a number with an exact zero imaginary part IS real. (exact 5.0+0.0i) is 5, not 5+0i.

This used to return NewBigComplex unconditionally, minting a 5+0i that reported real? #t and integer? #t yet was not eqv? to 5 -- while BigComplex.ToExact demoted correctly. Two ToExacts, one applying the rule and one not.

func (*Complex) ToInexact

func (p *Complex) ToInexact() Number

ToInexact returns this Complex unchanged since it is already inexact.

R7RS §6.2.6: inexact returns an inexact representation of its argument.

type Complex128Result

type Complex128Result struct {
	Value   complex128   // complex128 representation
	RealAcc big.Accuracy // Below / Exact / Above for real component
	ImagAcc big.Accuracy // Below / Exact / Above for imaginary component
}

Complex128Result captures complex-domain conversion with per-component accuracy. Field-named so RealAcc/ImagAcc swaps are caught at compile time, not surfaced only as wrong output in tests.

Zero value is {Value: 0+0i, RealAcc: Exact, ImagAcc: Exact} since big.Exact == 0 in the stdlib enum. That happens to coincide with the "perfectly converted zero" reading; callers receiving an error should still treat the result as unspecified.

See design plan §"Decision record: return shape — hybrid (positional + struct)".

func ToComplex128WithAccuracy

func ToComplex128WithAccuracy(n Number) (Complex128Result, error)

ToComplex128WithAccuracy is the primary complex-domain helper. Returns a Complex128Result struct (named fields prevent realAcc/imagAcc swap bugs at call sites; same-type adjacency would otherwise admit silent swaps the compiler can't catch).

For real-only inputs (Integer/BigInteger/Float/BigFloat/Rational), res.ImagAcc is always big.Exact.

For nil-Number defensive input, returns ErrNotANumber.

type ComplexNumber

type ComplexNumber interface {
	Number
	RealPart() Number
	ImagPart() Number
	IsReal() bool
}

ComplexNumber represents a complex-valued number with accessible parts.

R7RS §6.2.6: Complex numbers have real and imaginary parts accessible via real-part and imag-part.

type ConditionVariable

type ConditionVariable struct {
	// contains filtered or unexported fields
}

ConditionVariable represents a Scheme condition variable (SRFI-18)

func NewConditionVariable

func NewConditionVariable(name string) *ConditionVariable

NewConditionVariable creates a new condition variable

func (*ConditionVariable) Broadcast

func (p *ConditionVariable) Broadcast()

Broadcast wakes all waiting threads.

func (*ConditionVariable) EqualTo

func (p *ConditionVariable) EqualTo(v Value) bool

EqualTo returns true if the condition variables are the same object.

func (*ConditionVariable) ID

func (p *ConditionVariable) ID() uint64

ID returns the condition variable's unique identifier

func (*ConditionVariable) IsVoid

func (p *ConditionVariable) IsVoid() bool

IsVoid returns true if the condition variable is nil.

func (*ConditionVariable) Name

func (p *ConditionVariable) Name() string

Name returns the condition variable's name

func (*ConditionVariable) SchemeString

func (p *ConditionVariable) SchemeString() string

SchemeString returns the Scheme representation of this condition variable.

func (*ConditionVariable) SetSpecific

func (p *ConditionVariable) SetSpecific(v Value)

SetSpecific sets the condition variable's specific field

func (*ConditionVariable) Signal

func (p *ConditionVariable) Signal()

Signal wakes one waiting thread (FIFO order).

func (*ConditionVariable) Specific

func (p *ConditionVariable) Specific() Value

Specific returns the condition variable's specific field

func (*ConditionVariable) Wait

func (p *ConditionVariable) Wait(_ *Mutex, timeout *time.Duration) bool

Wait waits on the condition variable until signaled or (if timeout is non-nil) the timeout elapses. Returns true if signaled, false if timed out.

The *Mutex argument is unused: atomic unlock-and-wait is owned by Mutex.UnlockContext, which enqueues the waiter (registerWaiter) before releasing the mutex. Wait itself registers and blocks in one step, which is correct only for a caller that holds no mutex.

func (*ConditionVariable) WaiterCount

func (p *ConditionVariable) WaiterCount() int

WaiterCount returns the number of threads waiting on this condition variable.

type DebugLocation

type DebugLocation struct {
	File   string
	Line   int
	Column int
}

DebugLocation holds file/line/column for debug and error display. This is a simple struct for presentation layers (REPL, debugger UI), distinct from the SourceLocation interface which is a full Value type used by procedure-source-location.

type DebugState

type DebugState interface {
	// CurrentLocation returns the source location at the current
	// execution point, or nil if no source info is available.
	CurrentLocation() *DebugLocation

	// FormatStackTrace returns a human-readable stack trace string,
	// walking at most maxDepth frames.
	FormatStackTrace(maxDepth int) string
}

DebugState provides read-only access to VM execution state. Implemented by the VM's MachineContext; consumed by presentation layers (REPL, debugger UI) without importing machine/.

type DeepEqualer added in v1.19.0

type DeepEqualer interface {
	EqualComponents(other Value, push func(a, b Value)) bool
}

DeepEqualer is implemented by container values whose equality is defined by their components rather than by themselves. Equal owns the traversal; an implementor only decides whether the two containers are shaped alike and, if so, hands Equal the component pairs to compare.

EqualComponents MUST NOT compare components itself, directly or by calling EqualTo on them: that would put the recursion back on the Go stack, which is exactly what this interface exists to remove. Everything it can settle locally (wrong type, differing length, differing record type) it settles by returning false; everything else it pushes.

An implementor MUST be pointer-shaped, and so Go-comparable: Equal keys its visited set on (a, b) identity, and hashing a non-comparable dynamic type panics. Every implementor in this package is a pointer, and the wider Value contract already assumes as much — EqIdentity (utils.go), which backs eq?, compares interfaces with == and would fault on a slice- or map-backed Value long before equal? saw it.

A recursive value type defined by an embedder that does NOT implement DeepEqualer is compared through its own EqualTo, and a cycle in it will overflow the host stack. See docs/extensions/architecture.md.

type Exactness

type Exactness int

Exactness represents whether a number is exact or inexact.

R7RS §6.2.2: Numbers are either exact or inexact. A number is exact if it was written as an exact constant or derived from exact numbers using only exact operations. Otherwise, it is inexact.

const (
	Exact Exactness = iota
	Inexact
)

Exactness constants for R7RS exact/inexact classification.

func ExactnessOf

func ExactnessOf(n Number) Exactness

ExactnessOf returns the exactness of a number.

R7RS §6.2.2: - Integer, BigInteger, Rational are always exact (IsAlwaysExact in spec) - Float, BigFloat, Complex are always inexact (IsAlwaysExact == false) - BigComplex depends on its components (per-instance check via IsExact)

Panics on nil; nil cannot meaningfully classify as Exact or Inexact and indicates a caller bug.

type Float

type Float struct {
	Value float64
}

Float represents a Scheme floating-point number.

func NewFloat

func NewFloat(v float64) *Float

NewFloat creates a new float value.

func (*Float) Abs

func (p *Float) Abs() Number

Abs returns the absolute value of this float.

func (*Float) Add

func (p *Float) Add(o Number) Number

Add returns the sum of this Float and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: inexact + inexact = inexact, exact + inexact = inexact.

func (*Float) Divide

func (p *Float) Divide(o Number) (Number, error)

Divide returns the quotient of this float and another number.

func (*Float) EqualTo

func (p *Float) EqualTo(v Value) bool

EqualTo implements R7RS equal? for Float.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*Float) HashCode

func (p *Float) HashCode() uint64

HashCode returns a hash of the float value.

The Hashable contract is one-directional — equal implies same hash — and after the R7RS §6.1 alignment a Float is NEVER eqv? to a BigFloat (representation is observable for inexacts, so they are distinct numbers). The contract therefore says nothing about the two, and this hash owes cross-type agreement to nothing. It used to claim it "produce[d] identical hashes for equal values" across Float and BigFloat, which is now a promise about a relation that cannot hold.

What it DOES owe: every NaN hashes alike (eqv? identifies all NaNs, so the contract binds), and ±Inf stay bit-exact (+inf.0 and -inf.0 are NOT eqv?, so they must be able to differ).

func (*Float) IsExact

func (p *Float) IsExact() bool

IsExact returns false since Float is always inexact.

R7RS §6.2.2: Floating-point numbers are inexact.

func (*Float) IsFinite

func (p *Float) IsFinite() bool

IsFinite returns true if this float is finite (not Inf or NaN).

R7RS §6.2.6: finite? returns #t for finite numbers.

func (*Float) IsInteger

func (p *Float) IsInteger() bool

IsInteger returns true if this float represents an integer value.

R7RS §6.2.6: integer? returns #t for inexact integers (e.g., 3.0). Uses math.Trunc to correctly handle large floats outside int64 range.

func (*Float) IsNaN

func (p *Float) IsNaN() bool

IsNaN returns true if this float is NaN.

R7RS §6.2.6: nan? returns #t for NaN values.

func (*Float) IsNegative

func (p *Float) IsNegative() bool

IsNegative returns true if this float is negative.

func (*Float) IsPositive

func (p *Float) IsPositive() bool

IsPositive returns true if this float is positive.

func (*Float) IsRational

func (p *Float) IsRational() bool

IsRational returns true if this float is finite (not NaN or Inf).

R7RS §6.2.6: rational? returns #t for finite inexact reals.

func (*Float) IsVoid

func (p *Float) IsVoid() bool

IsVoid returns true if the float is nil.

func (*Float) IsZero

func (p *Float) IsZero() bool

IsZero returns true if this float is zero.

func (*Float) Kind

func (p *Float) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*Float) LessThan

func (p *Float) LessThan(o Number) bool

LessThan returns true if this float is less than another number.

func (*Float) Multiply

func (p *Float) Multiply(o Number) Number

Multiply returns the product of two numbers.

R7RS §6.2.6: The * procedure returns the product of its arguments.

Exactness: an exact zero annihilates the product to exact 0, regardless of the other operand (R7RS §6.2.2; matches Chez and Racket, including (* +inf.0 0) => 0). An inexact zero does not short-circuit: IEEE 754 governs, so (* 5 0.0) => 0.0 and (* -1.0 0.0) => -0.0.

func (*Float) Negate

func (p *Float) Negate() Number

Negate returns the negation of this float.

R7RS §6.2.6: The - procedure with one argument returns the additive inverse.

func (*Float) SchemeString

func (p *Float) SchemeString() string

SchemeString returns the Scheme representation of the float.

R7RS §6.2.5: +inf.0, -inf.0, and +nan.0 are the written representations for positive infinity, negative infinity, and NaN. R7RS §7.1.1: Inexact real numbers must contain a decimal point to distinguish them from exact integers.

func (*Float) Sign

func (p *Float) Sign() int

Sign returns -1 if negative, 0 if zero, or 1 if positive. NaN returns 0.

func (*Float) SignBit added in v1.19.0

func (p *Float) SignBit() bool

SignBit reports whether this float carries a negative sign bit, INCLUDING -0.0.

This is the case IsNegative cannot see: -0.0 < 0 is false, so IsNegative reports false for a value that is unambiguously on the negative side of the real axis. math.Signbit reads the bit itself.

func (*Float) String

func (p *Float) String() string

func (*Float) Subtract

func (p *Float) Subtract(o Number) Number

Subtract returns the difference of two numbers.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: inexact - inexact = inexact, exact - inexact = inexact.

func (*Float) ToExact

func (p *Float) ToExact() (Number, error)

ToExact converts this Float to an exact Number.

R7RS §6.2.6: exact returns an exact representation of its argument. Returns BigInteger if the float is integral, Rational otherwise.

func (*Float) ToInexact

func (p *Float) ToInexact() Number

ToInexact returns this Float unchanged since it is already inexact.

R7RS §6.2.6: inexact returns an inexact representation of its argument.

type Flusher

type Flusher interface {
	Flush() error
}

Flusher is the interface satisfied by buffered writers that can flush pending bytes to the underlying stream.

type ForEachFunc

type ForEachFunc func(ctx context.Context, i int, hasNext bool, v Value) error

ForEachFunc is the callback signature for iterating over a Tuple.

Parameters:

  • ctx: context for cancellation
  • i: zero-based element index
  • hasNext: true if more elements follow
  • v: the current element value

Return a non-nil error to stop iteration early.

type Hashable

type Hashable interface {
	Value
	HashCode() uint64
}

Hashable represents a Value that can be used as a hashtable key.

R7RS §6.10: Hashtables map keys to values. Keys are compared using equal?, and the hash function must be consistent with the equality predicate: if a.EqualTo(b) then a.HashCode() == b.HashCode().

Implemented by: Integer, BigInteger, Float, BigFloat, Rational, Complex, BigComplex, Boolean, Character, Symbol, Byte, String.

type Hashtable

type Hashtable struct {
	// contains filtered or unexported fields
}

Hashtable represents a Scheme hash table mapping hashable values to values.

Separate chaining (Cormen et al., CLRS Ch. 11): collisions are resolved by storing all entries with the same hash in a linked list (here, a Go slice). O(1) amortized with a good hash function. See BIBLIOGRAPHY.md "Separate Chaining Hash Table".

Keys must implement the Hashable interface (Value + HashCode()). Uses bucket chaining with FNV-1a hashing for O(1) amortized operations and EqualTo() for key comparison within buckets.

Concurrency: LOCK-FREE, by design. A Hashtable is user-owned data that SRFI-18 threads may share; per the concurrency ownership line, the USER synchronizes it for atomic multi-step sequences or a consistent iteration snapshot. The type itself carries no mutex — but it must never CRASH the host on concurrent access, so the backing store is a sync.Map (key: uint64 hash → an IMMUTABLE []hashtableEntry bucket) with copy-on-write writes:

  • Reads Load an immutable bucket and scan it — no lock, no data race.
  • Writes (Set/Delete) Load the bucket, COPY it, mutate the copy, Store the new slice. Buckets are never mutated in place, so the inner-slice race is gone too. Both halves (sync.Map AND copy) are required: sync.Map alone would leave an in-place slice append/overwrite racing.

The lock-free store removes Go's fatal "concurrent map read and map write"; what it does NOT provide is transactional atomicity. Under UNSYNCHRONIZED concurrent writers to one bucket a Set may be lost (last-Store-wins) and the atomic size may drift — that is the accepted consequence of the user not synchronizing their own shared data. Single-threaded use is exact.

func NewEmptyHashtable

func NewEmptyHashtable() *Hashtable

NewEmptyHashtable creates a new empty hash table. The zero sync.Map and atomic.Int64 are ready to use, so no field initialization is needed.

func (*Hashtable) Clear

func (p *Hashtable) Clear()

Clear removes all entries from the hash table.

func (*Hashtable) Copy

func (p *Hashtable) Copy() *Hashtable

Copy returns a shallow copy of the hash table. Buckets are immutable, so each stored slice can be shared directly with the copy without re-copying.

func (*Hashtable) Delete

func (p *Hashtable) Delete(key Value) error

Delete removes the entry for key from the hash table. Returns werr.ErrInvalidArgument if the key does not implement Hashable.

Copy-on-write: a shrunk bucket is a fresh slice; the last entry's removal drops the bucket key entirely.

func (*Hashtable) Entries

func (p *Hashtable) Entries(fn func(key Hashable, value Value) error) error

Entries iterates over all entries in the hash table, calling fn for each key-value pair. Iteration stops early if fn returns a non-nil error. This is more efficient than Keys()+Get() as it avoids intermediate allocations.

fn runs against a snapshot: it may be Scheme code that reads or mutates this same table (hashtable-walk). The snapshot is the iteration's view; entries added concurrently are not visited.

func (*Hashtable) EqualComponents added in v1.19.0

func (p *Hashtable) EqualComponents(o Value, push func(a, b Value)) bool

EqualComponents pairs this table's entries against the other's by key, then pushes the matched VALUES for Equal to compare. Keys are matched here rather than pushed because a key is always a leaf: no container type implements Hashable, so a key cannot carry a cycle. TestNoContainerIsHashable pins that invariant — adding HashCode() to *Pair or *Vector (what R6RS make-equal-hashtable wants) would put recursion back on the Go stack here.

Both tables are read through lock-free snapshots, so no lock is held during the comparison and two tables never contend.

func (*Hashtable) EqualTo

func (p *Hashtable) EqualTo(o Value) bool

EqualTo returns true if both hash tables have equal contents.

Keys and values take different routes, and the asymmetry is deliberate: keys are matched inside EqualComponents (a key is always a leaf — see below), while values are pushed onto Equal's iterative worklist, since a value may be a container and may be cyclic.

func (*Hashtable) Get

func (p *Hashtable) Get(key Value) (Value, bool, error)

Get retrieves the value associated with key. Returns the value and whether the key was found. Returns werr.ErrInvalidArgument if the key does not implement Hashable.

func (*Hashtable) HasKey

func (p *Hashtable) HasKey(key Value) (bool, error)

HasKey returns whether the key exists in the hash table. Returns werr.ErrInvalidArgument if the key does not implement Hashable.

func (*Hashtable) IsVoid

func (p *Hashtable) IsVoid() bool

IsVoid returns true if this hash table is nil.

func (*Hashtable) Keys

func (p *Hashtable) Keys() Tuple

Keys returns a list of all keys in the hash table.

func (*Hashtable) SchemeString

func (p *Hashtable) SchemeString() string

SchemeString returns the Scheme representation of this hash table.

func (*Hashtable) Set

func (p *Hashtable) Set(key Value, val Value) error

Set associates key with val in the hash table. Returns werr.ErrInvalidArgument if the key does not implement Hashable.

Copy-on-write: the target bucket is copied before it is changed, so a concurrent reader scanning the old bucket is never disturbed. See the type comment for the (non-transactional) concurrency contract.

func (*Hashtable) Size

func (p *Hashtable) Size() int

Size returns the number of entries in the hash table. Exact single-threaded; best-effort under unsynchronized concurrent mutation, and never negative even when the counter has drifted below zero.

func (*Hashtable) Values

func (p *Hashtable) Values() Tuple

Values returns a list of all values in the hash table.

type Immutable

type Immutable interface {
	Value
	// IsImmutable reports whether in-place mutation of this value is forbidden.
	IsImmutable() bool
}

Immutable is implemented by value types that store their immutability as an intrinsic, per-instance property — currently only *String (R7RS §6.7: literal strings and symbol->string results are immutable).

It exists so callers can ask "may this value be mutated in place?" without knowing the storage mechanism. Pair and Vector deliberately do NOT implement it: they are raw [2]Value / []Value types whose immutability is tracked in an engine-scoped side-set (see environment.ImmutableLiterals) to keep the dominant heap objects word-for-word minimal. The uniform query that spans both mechanisms is (*environment.ImmutableLiterals).IsImmutable.

type Integer

type Integer struct {
	Value int64
}

Integer represents a Scheme integer value.

R7RS §6.2.1: Integers are exact numbers in the numeric tower hierarchy:

number ⊃ complex ⊃ real ⊃ rational ⊃ integer

R7RS §6.2.2: Integer is always exact. Operations on exact numbers produce exact results when mathematically well-defined.

func NewInteger

func NewInteger(v int64) *Integer

NewInteger returns an Integer value. Small integers in the range -32768 to 32767 are cached and return the same pointer for the same value.

func (*Integer) Abs

func (p *Integer) Abs() Number

func (*Integer) Add

func (p *Integer) Add(o Number) Number

Add returns the sum of this Integer and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: exact + exact = exact, exact + inexact = inexact. When adding Integer + BigInteger, result is BigInteger (exact). When adding Integer + Float/Complex, result is Float/Complex (inexact).

func (*Integer) Divide

func (p *Integer) Divide(o Number) (Number, error)

Divide returns the quotient of this integer and another number.

R7RS §6.2.6: The / procedure returns the quotient of its arguments. For exact arguments, / may return a non-integer (Rational) when the mathematical result is not an integer. Returns Integer only when the division is exact.

R7RS §6.2.2 Exactness: exact / exact = exact (Integer or Rational), exact / inexact = inexact (Float or Complex).

func (*Integer) EqualTo

func (p *Integer) EqualTo(v Value) bool

EqualTo implements R7RS equal? for Integer.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*Integer) HashCode

func (p *Integer) HashCode() uint64

HashCode returns a hash of the integer value. Uses the canonical exact-family hash so that Integer, BigInteger, and Rational produce identical hashes for equal values.

func (*Integer) IsExact

func (p *Integer) IsExact() bool

IsExact returns true since Integer is always exact.

R7RS §6.2.2: Integers are always exact numbers.

func (*Integer) IsFinite

func (p *Integer) IsFinite() bool

IsFinite returns true since integers are always finite.

R7RS §6.2.6: finite? returns #t for all exact numbers.

func (*Integer) IsInteger

func (p *Integer) IsInteger() bool

IsInteger returns true since Integer is always an integer.

R7RS §6.2.6: integer? returns #t for exact integers.

func (*Integer) IsNaN

func (p *Integer) IsNaN() bool

IsNaN returns false since integers are never NaN.

R7RS §6.2.6: nan? returns #f for exact numbers.

func (*Integer) IsNegative

func (p *Integer) IsNegative() bool

IsNegative returns true if this integer is negative.

R7RS §6.2.6: negative? returns #t if the real number is negative.

func (*Integer) IsPositive

func (p *Integer) IsPositive() bool

IsPositive returns true if this integer is positive.

R7RS §6.2.6: positive? returns #t if the real number is positive.

func (*Integer) IsRational

func (p *Integer) IsRational() bool

IsRational returns true since integers are a subset of rationals.

R7RS §6.2.6: rational? returns #t for all real finite numbers.

func (*Integer) IsVoid

func (p *Integer) IsVoid() bool

IsVoid returns true if this integer is nil.

func (*Integer) IsZero

func (p *Integer) IsZero() bool

IsZero returns true if this integer is zero.

func (*Integer) Kind

func (p *Integer) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*Integer) LessThan

func (p *Integer) LessThan(o Number) bool

LessThan returns true if this integer is less than another number.

R7RS §6.2.6: The < procedure returns #t if its arguments are monotonically increasing. Comparison across numeric types uses mathematical value.

func (*Integer) Multiply

func (p *Integer) Multiply(o Number) Number

Multiply returns the product of this integer and another number.

R7RS §6.2.6: The * procedure returns the product of its arguments. R7RS §6.2.2 Exactness: exact * exact = exact, exact * inexact = inexact. Exception: Exact zero dominates—(* 0 x) may return exact 0 even when x is inexact. Zero is an exact value when the result is mathematically unambiguous. This implementation follows Chez Scheme's behavior.

func (*Integer) Negate

func (p *Integer) Negate() Number

Negate returns the negation of this integer.

R7RS §6.2.6: The - procedure with one argument returns the additive inverse.

func (*Integer) SchemeString

func (p *Integer) SchemeString() string

SchemeString returns the Scheme representation of this integer.

func (*Integer) Sign

func (p *Integer) Sign() int

Sign returns -1 if negative, 0 if zero, or 1 if positive.

func (*Integer) SignBit added in v1.19.0

func (p *Integer) SignBit() bool

SignBit reports whether this integer carries a negative sign bit.

Integer is exact, so it has no signed zero and this coincides with IsNegative.

func (*Integer) Subtract

func (p *Integer) Subtract(o Number) Number

Subtract returns the difference of this integer and another number.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: exact - exact = exact, exact - inexact = inexact.

func (*Integer) ToExact

func (p *Integer) ToExact() (Number, error)

ToExact returns this Integer unchanged since it is already exact.

R7RS §6.2.6: exact returns an exact representation of its argument.

func (*Integer) ToInexact

func (p *Integer) ToInexact() Number

ToInexact converts this Integer to an inexact Float.

R7RS §6.2.6: inexact returns an inexact representation of its argument.

type JoinTimeoutException

type JoinTimeoutException struct{}

JoinTimeoutException is raised when thread-join! times out

func (*JoinTimeoutException) Error

func (p *JoinTimeoutException) Error() string

type Mutex

type Mutex struct {
	// contains filtered or unexported fields
}

Mutex represents a Scheme mutex (SRFI-18)

func NewMutex

func NewMutex(name string) *Mutex

NewMutex creates a new unlocked mutex

func (*Mutex) EqualTo

func (p *Mutex) EqualTo(v Value) bool

EqualTo returns true if the mutexes are the same object.

func (*Mutex) ID

func (p *Mutex) ID() uint64

ID returns the mutex's unique identifier

func (*Mutex) IsVoid

func (p *Mutex) IsVoid() bool

IsVoid returns true if the mutex is nil.

func (*Mutex) Lock

func (p *Mutex) Lock(timeout *time.Duration, owner *Thread) (bool, error)

Lock acquires the mutex with optional timeout and owner, without ctx cancellation. It is LockContext with a background ctx — for callers that have no ctx to thread (tests, internal helpers). VM primitives use LockContext so a terminated thread parked here is unparked.

func (*Mutex) LockContext added in v1.19.0

func (p *Mutex) LockContext(ctx context.Context, timeout *time.Duration, owner *Thread) (bool, error)

LockContext acquires the mutex with optional timeout and owner. Returns true if acquired, false if the wait ended without acquiring (timeout or ctx cancellation).

When acquired, state becomes MutexLocked and owner is set to whatever the caller supplied (nil produces a "locked-but-unowned" mutex, valid per SRFI-18). Acquiring an abandoned mutex succeeds but returns *AbandonedMutexException so the caller can observe the prior owner's termination.

ctx cancellation unparks the untimed wait so a thread blocked here is reaped by thread-terminate! rather than stalling on a bare cond.Wait — the same wait-side fix the RWMutex type carries. Unlike the rw-mutex primitives (whose shared helper finishBlockingSync raises ErrOperationCancelled on a false return), mutex-lock! reports a cancelled acquire as #f: that primitive already signals "did not acquire" as #f, and returning it error-free lets a wrapping with-timeout handler run via callForeignCached's recheck without a carve-out. The held side is untouched: a terminated holder's lock stays held (abandonment is a separate, owner-driven path via MarkAbandoned).

func (*Mutex) MarkAbandoned

func (p *Mutex) MarkAbandoned()

MarkAbandoned marks the mutex as abandoned (called when owner thread terminates). Only mutexes in MutexLocked state can be abandoned — unlocked and already- abandoned mutexes are no-ops.

func (*Mutex) Name

func (p *Mutex) Name() string

Name returns the mutex's name

func (*Mutex) Owner

func (p *Mutex) Owner() *Thread

Owner returns the current owner thread, or nil if not owned

func (*Mutex) SchemeString

func (p *Mutex) SchemeString() string

SchemeString returns the Scheme representation of the mutex.

func (*Mutex) SetSpecific

func (p *Mutex) SetSpecific(v Value)

SetSpecific sets the mutex's specific field

func (*Mutex) Specific

func (p *Mutex) Specific() Value

Specific returns the mutex's specific field

func (*Mutex) State

func (p *Mutex) State() MutexState

State returns the current state of the mutex

func (*Mutex) StateValue

func (p *Mutex) StateValue() Value

StateValue returns the state as a Scheme value per R7RS SRFI-18. Returns package-level singletons for symbol states; the singletons avoid re-allocating the symbol, and eq? on symbols is by name (see EqIdentity). Returns: 'not-owned, 'abandoned, or the owner thread.

SRFI-18 collapses "unlocked" and "locked without owner" into the single 'not-owned symbol — they are indistinguishable to Scheme. The Go-side distinction is preserved by MutexState (Unlocked is acquirable without blocking; Locked-without-owner is held by a non-thread caller).

func (*Mutex) Unlock

func (p *Mutex) Unlock(cv *ConditionVariable, timeout *time.Duration) bool

Unlock releases the mutex without ctx cancellation, delegating to UnlockContext with a background ctx — for callers with no ctx to thread (tests, internal helpers). VM primitives use UnlockContext so a thread parked on the condition variable is reaped by thread-terminate!.

func (*Mutex) UnlockContext added in v1.19.0

func (p *Mutex) UnlockContext(ctx context.Context, cv *ConditionVariable, timeout *time.Duration) bool

UnlockContext releases the mutex. If cv is non-nil it performs the SRFI-18 atomic unlock-and-wait: the waiter is enqueued on cv BEFORE the mutex is released, so an idiomatic signaller (which holds this mutex while changing the predicate) cannot signal an empty wait set and lose the wakeup. ctx cancellation unparks the cv wait so thread-terminate! reaps a thread blocked here.

cv.mu and this mutex's internal lock are never held simultaneously, so no lock-order inversion arises. A non-idiomatic signaller that signals WITHOUT holding the mutex can still race ahead of registerWaiter; SRFI-18 requires the mutex be held, and closing that window would nest cv.mu inside p.mu and re-introduce a lock-order edge.

type MutexState

type MutexState int

MutexState represents the lifecycle state of a mutex.

The owned-vs-not-owned distinction (R7RS SRFI-18) is NOT a state — it's the contents of the owner field. Splitting "locked with owner" and "locked without owner" into separate states would force every site that reads state to also know which states permit owner != nil. Instead, MutexLocked is one state; owner = nil iff acquired without owner.

Invariants enforced by Lock/Unlock/MarkAbandoned:

state == MutexUnlocked   ⇒ owner == nil
state == MutexLocked     — owner is the identity (nil ⇒ "not-owned")
state == MutexAbandoned  ⇒ owner == nil
const (
	MutexUnlocked  MutexState = iota // Not locked
	MutexLocked                      // Held
	MutexAbandoned                   // Owner terminated while holding lock
)

MutexState constants. See the invariant block above for state↔owner relations.

func (MutexState) String

func (p MutexState) String() string

type NamedTypeConstraint

type NamedTypeConstraint struct {
	// contains filtered or unexported fields
}

NamedTypeConstraint represents an unresolved type name from a docstring (e.g., "point"). It is documentation-only — Check always fails because the constraint has not been resolved to a concrete type.

func NewNamedTypeConstraint

func NewNamedTypeConstraint(name string) *NamedTypeConstraint

NewNamedTypeConstraint creates a NamedTypeConstraint with the given name.

func (*NamedTypeConstraint) Check

func (p *NamedTypeConstraint) Check(v Value) (any, bool, error)

Check always fails — the constraint is unresolved and cannot validate values.

func (*NamedTypeConstraint) Description

func (p *NamedTypeConstraint) Description() string

Description returns the unresolved type name as its description.

func (*NamedTypeConstraint) Name

func (p *NamedTypeConstraint) Name() string

Name returns the unresolved type name.

type NativeError

type NativeError struct {
	// contains filtered or unexported fields
}

NativeError represents an R7RS error object created by (error ...). It contains a message string and a list of irritant objects that provide additional context about the error. It can also wrap a Go error.

func NewErrorObject

func NewErrorObject(message string, irritants ...Value) *NativeError

NewErrorObject creates a new error object with the given message and irritants.

func NewErrorObjectWithCause

func NewErrorObjectWithCause(message string, cause error, irritants ...Value) *NativeError

NewErrorObjectWithCause creates a new error object that wraps a Go error. This preserves the original error for debugging while providing R7RS-compliant exception handling. The wrapped error can be retrieved with Datum() or Unwrap().

func NewErrorObjectWithCauseAndKind

func NewErrorObjectWithCauseAndKind(message string, cause error, kind NativeErrorKind, irritants ...Value) *NativeError

NewErrorObjectWithCauseAndKind creates a new error object that wraps a Go error with a specific kind. R7RS §6.11: The kind determines which error predicate (file-error?, read-error?) matches.

func NewFileError

func NewFileError(message string, irritants ...Value) *NativeError

NewFileError creates a new file error object with the given message and irritants. R7RS §6.11: file-error? predicate checks for errors during file operations.

func NewNativeError

func NewNativeError(msg string) *NativeError

NewNativeError creates a new native error with the given message.

func NewReadError

func NewReadError(message string, irritants ...Value) *NativeError

NewReadError creates a new read error object with the given message and irritants. R7RS §6.11: read-error? predicate checks for errors during reading.

func (*NativeError) EqualComponents added in v1.19.0

func (p *NativeError) EqualComponents(v Value, push func(a, b Value)) bool

EqualComponents compares message, kind and the wrapped Go error directly, and pushes the irritant lists for Equal to descend. Raise-site state (sourceLocation, stackTraceVal) is deliberately not compared: it records where an object was raised, not what it is. Irritants are arbitrary Scheme values, so an irritant graph can cycle back to the error object itself — reachable from pure R7RS with error + guard + set-car!. Descending here rather than re-entering Equal is what lets the visited set close that cycle; comparing the irritants inline allocated a fresh visited set per level and overflowed the host stack.

func (*NativeError) EqualTo

func (p *NativeError) EqualTo(v Value) bool

EqualTo returns true if this error object is equal to the given value.

func (*NativeError) Error

func (p *NativeError) Error() string

Error implements the error interface.

func (*NativeError) Irritants

func (p *NativeError) Irritants() Value

Irritants returns the list of irritant objects.

func (*NativeError) IsFileError

func (p *NativeError) IsFileError() bool

IsFileError returns true if this is a file error.

func (*NativeError) IsReadError

func (p *NativeError) IsReadError() bool

IsReadError returns true if this is a read error.

func (*NativeError) IsVoid

func (p *NativeError) IsVoid() bool

IsVoid returns true if this error object is nil.

func (*NativeError) Kind

func (p *NativeError) Kind() NativeErrorKind

Kind returns the error kind for R7RS error predicates.

func (*NativeError) Message

func (p *NativeError) Message() *String

Message returns the error message string.

func (*NativeError) SchemeString

func (p *NativeError) SchemeString() string

SchemeString returns the Scheme string representation of this error object.

func (*NativeError) SetSourceLocation

func (p *NativeError) SetSourceLocation(loc string)

SetSourceLocation sets the source location string.

func (*NativeError) SetStackTraceValue

func (p *NativeError) SetStackTraceValue(v Value)

SetStackTraceValue sets the stack trace Scheme value.

func (*NativeError) SourceLocation

func (p *NativeError) SourceLocation() string

SourceLocation returns the formatted source location string, or "".

func (*NativeError) StackTraceValue

func (p *NativeError) StackTraceValue() Value

StackTraceValue returns the stack trace as a Scheme value, or nil.

func (*NativeError) Unwrap

func (p *NativeError) Unwrap() error

Unwrap returns the underlying Go error for errors.Unwrap compatibility.

type NativeErrorKind

type NativeErrorKind int

NativeErrorKind represents the type of an error object for R7RS error predicates.

const (
	// NativeErrorKindGeneric is a generic error (default).
	NativeErrorKindGeneric NativeErrorKind = iota
	// NativeErrorKindRead is a read error (from reading data).
	NativeErrorKindRead
	// NativeErrorKindFile is a file error (from file operations).
	NativeErrorKindFile
)

type Number

type Number interface {
	Value
	Kind() NumericKind
	Add(Number) Number
	Subtract(Number) Number
	Multiply(Number) Number
	Divide(Number) (Number, error)
	Negate() Number
	Abs() Number
	ToExact() (Number, error)
	ToInexact() Number
	IsZero() bool
	IsExact() bool
	IsInteger() bool  // R7RS §6.2.6: is this an integer value?
	IsRational() bool // R7RS §6.2.6: is this a rational value?
	IsFinite() bool   // R7RS §6.2.6: is this a finite number?
	IsNaN() bool      // R7RS §6.2.6: is this NaN?

	// LessThan is the tower's ONLY ordering primitive, and deliberately so.
	//
	// A NaN operand yields false in both directions, which is exactly right: NaN
	// is unordered against everything including itself, and bool has no third
	// state to get wrong. Equality is therefore not a comparison result here but
	// a separate question — ask EqvNumber, which owns it (eqv.go).
	//
	// There used to be a Compare(Number) int alongside this. It answered a
	// four-state question (less, equal, greater, unordered) in a three-state
	// return, so a NaN got 0 and read as "equal": `Compare(o) == 0` reported NaN
	// equal to everything, and a comparator built on it was not a total order at
	// all. It had no consumer outside this package, and its only two consumers
	// inside it wanted equality, which was the one thing it could not say. See
	// numEqual (eqv.go) for how equality is spelled now.
	LessThan(Number) bool
}

Number represents a numeric value in the Scheme numeric tower.

R7RS §6.2.1: Numbers form a tower: number ⊃ complex ⊃ real ⊃ rational ⊃ integer. All numeric types implement this interface for uniform arithmetic operations.

Error signaling

Arithmetic methods signal errors by panicking with a static sentinel error (e.g., werr.ErrDivisionByZero, werr.ErrNotANumber). This follows the same convention used by Go's math/big package, where (*big.Int).Div, (*big.Int).QuoRem, and (*big.Float).Quo all panic on division by zero, and mirrors Go's own runtime behavior for built-in integer division.

Divide returns (Number, error) so callers can propagate division-by-zero without panic/recover. All other arithmetic methods remain single-return because they cannot fail.

func Promote

func Promote(n Number, target NumericKind) Number

Promote converts a Number to the target NumericKind using the promoter table. Conversions to Float/Complex from an exact kind are the lossy contagion promotions; all others are lossless. Panics if no promotion path exists (indicates a bug in the promotion table — all reachable paths should be populated).

func Simplify

func Simplify(n Number) Number

Simplify attempts to reduce a number to a simpler type without losing information.

Simplification rules:

  • BigComplex with an EXACT zero imaginary → real part (cross-kind; handled here)
  • All other per-kind descents are delegated to the NumericTypeSpec.SimplifyDown function registered for each kind (see values/numeric_registry.go).

Exactness, not magnitude, licenses the complex→real descent, exactly as in maybeSimplify and IsReal. Demoting on an INEXACT zero imaginary part would lose information twice over, which is what this function exists not to do: it drops a component R7RS §6.2.6 says is still there (real? -2.5+0.0i is #f), and it then launders the inexact real into an exact one, breaking the "must not change exactness class" constraint stated in the exact-zero rule's contract (exact_zero.go).

A *Complex therefore never descends: its parts are float64, so IsExact() is false unconditionally (complex.go) and a 0.0 imaginary part is always an inexact zero. There is no exact-zero-imag *Complex to descend from.

Returns nil unchanged (callers may pass nil from generic Value paths).

type NumericKind

type NumericKind uint8

NumericKind identifies a concrete numeric type for dispatch table indexing.

Used by the receiver-centric dispatch tables in each numeric type file to replace 7-way type switches with O(1) array lookups.

ADDING A NEW NUMERIC TYPE requires updates in these locations:

  1. values/numeric_kind.go — add KindXxx constant (this file); bump numKinds implicitly
  2. values/xxx.go — new type file: implement Number interface, declare [numKinds] dispatch tables, register via init() calling makeXxxDispatch helpers
  3. values/xxx.go — register a NumericTypeSpec in the same init() via registerNumericSpec(KindXxx, NumericTypeSpec{...}). Provide the per-kind helper functions (xxxSimplifyDown, xxxToFloat64WithAccuracy, and either xxxToComplex128WithAccuracy or set isAlwaysReal=true to have the registry auto-derive the complex helper via liftRealToComplex128). Provide the schemeName + isAlwaysExact metadata. The registry-driven cold-path functions (Simplify, ExactnessOf, NumberToFloat64, NumberToComplex128Lossy) pick up the new kind automatically.
  4. values/promotion.go — add row/column in promotionTable and promoter
  5. values/numeric_dispatch_test.go — add new dispatch tables to TestAllDispatchEntriesPopulated
  6. values/numeric_registry_test.go — add the new kind to equivalenceExemplars()
  7. registry/helpers/value_conv.go — update ToComplex128, ToFloat64
  8. extensions/math/prim_conversion.go — update exact->inexact, number->string, etc.
  9. extensions/math/prim_complex.go — update make-rectangular, make-polar, etc.
  10. parser/parser_number.go — if the type can be parsed from source
  11. registry/helpers/equality.go — update Eqv if the type has special eqv? semantics

Several historically-manual cold paths are now derived from the item-3 registry (Simplify, ExactnessOf, NumberToFloat64, NumberToComplex128Lossy), so the registration in item 3 is usually enough — the surviving sites above are the ones the registry does not yet cover.

The dispatch tables (item 2) are tested by TestAllDispatchEntriesPopulated. The NumericTypeSpec registration (item 3) is enforced eagerly at package init: a missing or incomplete registration panics with ErrNumericRegistry at process startup.

const (
	KindInteger NumericKind = iota
	KindBigInteger
	KindFloat
	KindBigFloat
	KindRational
	KindComplex
	KindBigComplex
)

type NumericTypeSpec

type NumericTypeSpec struct {
	// contains filtered or unexported fields
}

NumericTypeSpec records the cold-path properties of one numeric kind.

Each of the seven concrete numeric types registers exactly one spec via registerNumericSpec() in its init() function. Fields are unexported; callers use the getter methods.

The three function fields are non-nil invariants enforced by registerNumericSpec — bottom-of-chain kinds bind an identity simplifyDown rather than nil. Real-only kinds may omit toComplex128WithAccuracy and set isAlwaysReal=true; registerNumericSpec will auto-derive it from toFloat64WithAccuracy via liftRealToComplex128.

func LookupNumericSpec

func LookupNumericSpec(kind NumericKind) *NumericTypeSpec

LookupNumericSpec returns the NumericTypeSpec for the given kind. Bounds-checked: out-of-range kind panics with ErrNumericRegistry rather than producing a Go runtime "index out of range" panic. Consulted by the cold-path helpers (Simplify, ExactnessOf, NumberToFloat64, NumberToComplex128Lossy). NumberToFloat64/NumberToComplex128Lossy are also reached from the IEEE 754 special-value guard inside the arithmetic dispatch closures; those fire only when a Float operand is Inf/NaN, not on every arithmetic op. They are also reached unconditionally by the real ⊕ complex dispatch closures (promotion.go), which convert the real receiver on every such operation.

func (*NumericTypeSpec) IsAlwaysExact

func (p *NumericTypeSpec) IsAlwaysExact() bool

IsAlwaysExact reports whether every value of this kind is exact. BigComplex returns false; per-instance exactness is determined by BigComplex.IsExact() (called by ExactnessOf).

func (*NumericTypeSpec) IsAlwaysReal added in v1.19.0

func (p *NumericTypeSpec) IsAlwaysReal() bool

IsAlwaysReal reports whether every value of this kind is a real number, i.e. carries no imaginary component at all. True for Integer, BigInteger, Rational, Float, and BigFloat; false for Complex and BigComplex.

This is NOT the same question as R7RS real?, and it must not be used to answer it: a BigComplex with an exact zero imaginary part IS real? (see IsReal), yet its kind is not always-real. The distinction this predicate captures is structural — does the KIND have an imaginary slot — and it survives promotion only if it is asked BEFORE promotion, because lifting a real into the complex LUB manufactures a zero imaginary part that is indistinguishable from a user-written one.

func (*NumericTypeSpec) SchemeName

func (p *NumericTypeSpec) SchemeName() string

SchemeName returns the Scheme type name for this numeric kind (e.g. "integer").

func (*NumericTypeSpec) SimplifyDown

func (p *NumericTypeSpec) SimplifyDown(n Number) Number

SimplifyDown reduces n to the simplest in-kind representation in a single call (multi-step descents are inlined per-kind: e.g. BigFloat→BigInteger→Integer). Returns n unchanged if no simpler representation exists. The cross-kind BigComplex/Complex shortcuts live in Simplify() itself, not here.

func (*NumericTypeSpec) ToComplex128WithAccuracy

func (p *NumericTypeSpec) ToComplex128WithAccuracy(n Number) Complex128Result

ToComplex128WithAccuracy dispatches via the registered closure for the kind. Returns a Complex128Result with per-component accuracy. For real-only inputs, res.ImagAcc is big.Exact.

func (*NumericTypeSpec) ToFloat64WithAccuracy

func (p *NumericTypeSpec) ToFloat64WithAccuracy(n Number) (float64, big.Accuracy, bool)

ToFloat64WithAccuracy dispatches via the registered closure for the kind. Returns (value, accuracy, isReal). The accuracy slot is Below/Exact/Above per Go big.Accuracy semantics. isReal is false iff the input was a Complex/BigComplex with non-zero imaginary part (the imaginary component is dropped; callers should use ToComplex128WithAccuracy for full fidelity).

type Once

type Once struct {
	// contains filtered or unexported fields
}

Once wraps sync.Once for Scheme

func NewOnce

func NewOnce() *Once

NewOnce creates a new Once

func (*Once) Do

func (p *Once) Do(f func()) bool

Do calls the function only once Returns true if this call executed the function, false if it was already called

func (*Once) Done

func (p *Once) Done() bool

Done reports whether Do's function has run to completion. It is false while the function is executing, and stays false if the function panicked, even though the Once is then consumed and further Do calls return false.

func (*Once) EqualTo

func (p *Once) EqualTo(v Value) bool

EqualTo returns true if the onces are the same object.

func (*Once) ID

func (p *Once) ID() uint64

ID returns the Once's unique identifier

func (*Once) IsVoid

func (p *Once) IsVoid() bool

IsVoid returns true if the once is nil.

func (*Once) SchemeString

func (p *Once) SchemeString() string

SchemeString returns the Scheme representation of the once.

type OpaqueValue

type OpaqueValue struct {
	// contains filtered or unexported fields
}

OpaqueValue wraps an arbitrary Go object as a Scheme value. Construction is Go-only via NewOpaqueValue. The inner value is accessible only from Go via Unwrap.

func NewOpaqueValue

func NewOpaqueValue(tag string, val any) *OpaqueValue

NewOpaqueValue creates a new opaque value with the given tag and inner value.

func (*OpaqueValue) EqualTo

func (p *OpaqueValue) EqualTo(v Value) bool

EqualTo returns true only if both are the same object (identity equality).

func (*OpaqueValue) IsVoid

func (p *OpaqueValue) IsVoid() bool

IsVoid returns true if this opaque value is nil.

func (*OpaqueValue) OpaqueTag

func (p *OpaqueValue) OpaqueTag() string

OpaqueTag returns the tag string identifying this opaque value's kind.

func (*OpaqueValue) SchemeString

func (p *OpaqueValue) SchemeString() string

SchemeString returns the Scheme representation of this opaque value.

func (*OpaqueValue) Unwrap

func (p *OpaqueValue) Unwrap() any

Unwrap returns the inner Go value. Go-only — not exposed to Scheme.

type OriginInfo

type OriginInfo struct {
	Identifier       string         // Macro name that caused expansion (e.g., "let", "my-macro")
	ApplicationID    uint64         // Unique ID for this macro invocation (from intro scope)
	Location         *SourceContext // Where the macro was invoked (use-site)
	TemplateLocation *SourceContext // Where the macro template was defined (definition-site)
	Parent           *OriginInfo    // Previous link in origin chain (for nested macros)
}

OriginInfo tracks macro expansion chains for debugging and error reporting. Each OriginInfo represents one macro expansion in the chain, enabling:

  • Tracing generated code back to the macro that created it
  • Identifying which invocation (by unique ID) produced specific code
  • Locating the template source that was expanded

func (*OriginInfo) Depth

func (p *OriginInfo) Depth() int

Depth returns the length of the origin chain.

type Pair

type Pair [2]Value

Pair represents a Scheme cons cell.

Initial algebra (Bird & de Moor 1997, Meijer et al. 1991). Proper lists are the initial algebra of a polynomial functor.

List = μX. 1 + Value × X

Constructors:
  nil  : 1 → List          = EmptyList (emptyListType, not *Pair)
  cons : Value × List → List = NewCons(car, cdr)

Eliminator (catamorphism / fold):
  ForEach(f) applies f to each car, returns tail

Invariant: EmptyList is a separate Go type from *Pair. This encodes
  the two constructors as distinct injections: (pair? '()) → #f.
Constrains: IsList (must terminate — uses Floyd cycle detection),
  PairBlock (batch allocation optimization, does not change the algebra),
  all list-processing primitives (must handle both constructors).
Constrained by: Tuple interface (read-only view over both constructors).

See BIBLIOGRAPHY.md "Lists as Initial Algebras".

func NewCons

func NewCons(car, cdr Value) *Pair

NewCons creates a new Pair with the given car and cdr Values.

func (*Pair) AsVector

func (p *Pair) AsVector() *Vector

AsVector converts the Pair representing a proper list into a Vector. It panics if the Pair does not represent a proper list.

Consumes Spine. See Length for the circular-list caveat.

func (*Pair) Car

func (p *Pair) Car() Value

Car returns the car of the Pair.

func (*Pair) Cdr

func (p *Pair) Cdr() Value

Cdr returns the cdr of the Pair.

func (*Pair) EqualComponents added in v1.19.0

func (p *Pair) EqualComponents(o Value, push func(a, b Value)) bool

EqualComponents pushes the two pairs' cars and cdrs for Equal to compare.

The cdr is pushed BEFORE the car so that the worklist, which pops last-in first, drains the car's subtree before walking on down the spine. Pushing car first would queue one pending entry per spine element, making a flat list cost O(n) auxiliary space instead of O(1).

func (*Pair) EqualTo

func (p *Pair) EqualTo(o Value) bool

EqualTo checks if the Pair is equal to another Value o. Delegates to Equal, which owns the iterative traversal and terminates on circular lists.

func (*Pair) ForEach

func (p *Pair) ForEach(ctx context.Context, fn ForEachFunc) (Value, error)

ForEach iterates over each element in the list represented by the Pair. The provided function fn is called for each element with the index i, a boolean hasNext indicating if there are more elements, and the value v. If fn returns an error, the iteration stops and the error is returned. If the list ends with a non-empty cdr, that cdr is returned as the first return value; a proper list returns EmptyList.

Two further error returns: ctx.Err() when the embedder's context is cancelled, and a wrapped werr.ErrCircularList when Brent's cycle detection fires. Unlike Length and AsVector, ForEach terminates on circular input.

Stays open-coded rather than consuming Spine: a Spine-consuming variant was measured ~40–56% slower across 10/100/1000-element lists (BenchmarkPairForEach in pair_bench_test.go) because each iter.Seq2 yield goes through two function pointers, and ForEach is hot enough that the per-step overhead dominates. The C.3/C.4 spine consumers (IsList, Length, AsVector) are called far less often per list, so their regression is invisible.

func (*Pair) IsEmptyList

func (p *Pair) IsEmptyList() bool

IsEmptyList returns false. A *Pair is never the empty list; EmptyList is a separate emptyListType value.

func (*Pair) IsList

func (p *Pair) IsList() bool

IsList checks if the Pair represents a proper list. Uses Floyd's cycle detection (tortoise-and-hare) to handle circular lists. Returns false for circular lists per R7RS §6.4. See BIBLIOGRAPHY.md "Floyd's Cycle Detection".

Consumes SpineWithCycleCheck: a cycle short-circuits the spine, and the final cell's cdr is checked for EmptyList to distinguish proper from improper termination.

func (*Pair) IsVoid

func (p *Pair) IsVoid() bool

IsVoid checks if the Pair is void (nil).

func (*Pair) Length

func (p *Pair) Length() int

Length returns the length of the list represented by the Pair. It panics if the Pair does not represent a proper list.

Consumes Spine. Callers must ensure the receiver is a proper list (e.g., via IsList) — a circular list will hang indefinitely because Spine does not detect cycles.

func (*Pair) SchemeString

func (p *Pair) SchemeString() string

SchemeString returns the Scheme representation of the Pair. Handles circular structures (from datum labels or set-cdr!/set-car!) by emitting "..." when a cycle is detected.

func (*Pair) SetCar

func (p *Pair) SetCar(v Value)

SetCar sets the car of the Pair to the given Value v.

func (*Pair) SetCdr

func (p *Pair) SetCdr(v Value)

SetCdr sets the cdr of the Pair to the given Value v.

func (*Pair) String

func (p *Pair) String() string

String returns the string representation of the Pair. Handles circular structures (from datum labels or set-cdr!/set-car!) by emitting "..." when a cycle is detected.

type PairBlock

type PairBlock []Pair

PairBlock is a contiguous slice of Pairs that can be linked into a proper list. Block allocation amortizes N heap allocations to 1 for list construction.

func (PairBlock) LinkSpine

func (b PairBlock) LinkSpine() PairBlock

LinkSpine links the block's cdrs into a proper-list spine and terminates the final cdr with EmptyList, leaving every car unset for the caller to fill. Use this when the cars must be assigned in an order LinkWith cannot express — e.g. reverse fills them back-to-front during a streaming walk. This is the single home for the spine-linking invariant; LinkWith builds on it. A nil or empty block is a no-op. Returns the block for chaining.

func (PairBlock) LinkWith

func (b PairBlock) LinkWith(vs []Value) Tuple

LinkWith fills cars from vs and links cdrs into a proper list, returning the head as a Tuple. The block must have the same length as vs. A nil or empty block returns EmptyList.

type Port

type Port interface {
	Value
	Close() error
	IsClosed() bool
}

Port represents a Scheme I/O port — the marker interface satisfied by any port value. Concretely implemented by *PortObject (the sole implementer; capability-conditional operations are reached via As*() (T, bool) accessors on *PortObject rather than narrower interfaces).

R7RS §6.13: All port types support close and open-state queries.

type PortObject

type PortObject struct {
	// contains filtered or unexported fields
}

PortObject is the single concrete representation of an R7RS port. Capability presence is encoded as nil-checks on the slot fields: a non-nil rdr means the port is readable, a non-nil wb means it supports byte writes, etc. Construction always goes through one of the New*Port factories, which call Validate before returning.

Field-set invariants are documented on Validate. The kind tag and slot configuration are paired by construction (every factory writes both in the same function body, adjacent to one another); Validate enforces only the cross-slot invariants I1–I7.

func NewBinaryInputPort

func NewBinaryInputPort(rdr *bufio.Reader) *PortObject

NewBinaryInputPort wraps an existing *bufio.Reader as a binary input port (R7RS §6.13.2 binary port).

func NewBinaryInputPortFromReader

func NewBinaryInputPortFromReader(reader io.Reader) *PortObject

NewBinaryInputPortFromReader buffers reader and constructs a binary input port. If reader implements io.Closer, Close is propagated.

func NewBinaryOutputPortFromWriter

func NewBinaryOutputPortFromWriter(writer io.Writer) *PortObject

NewBinaryOutputPortFromWriter buffers writer and constructs a binary output port. ws is intentionally nil — R7RS forbids write-string on binary ports even though *bufio.Writer satisfies io.StringWriter. flsh is non-nil so Close flushes before closing.

func NewByteVectorBufferedOutputPort

func NewByteVectorBufferedOutputPort() *PortObject

NewByteVectorBufferedOutputPort creates a fresh in-memory bytevector output port. flsh is nil. ext extracts the accumulated bytevector (returns the bytes verbatim — no EOF on empty, matching the prior ByteVectorBufferedOutputPort semantics).

func NewByteVectorBufferedOutputPortFromBuffer

func NewByteVectorBufferedOutputPortFromBuffer(buf *bytes.Buffer) *PortObject

NewByteVectorBufferedOutputPortFromBuffer wraps the given buffer as a bytevector buffered output port.

func NewByteVectorInputOutputPort

func NewByteVectorInputOutputPort() *PortObject

NewByteVectorInputOutputPort creates a fresh bidirectional bytevector port. Both sides share a *bytes.Buffer.

func NewByteVectorInputOutputPortFromBuffer

func NewByteVectorInputOutputPortFromBuffer(buf *bytes.Buffer) *PortObject

NewByteVectorInputOutputPortFromBuffer wraps the given buffer as a bidirectional bytevector port. ext returns io.EOF when the buffer is empty (preserving the prior ByteVectorInputOutputPort semantics where empty is signalled differently from BufferedOutputPort).

func NewByteVectorInputPortFromReader

func NewByteVectorInputPortFromReader(reader io.Reader) *PortObject

NewByteVectorInputPortFromReader buffers reader and constructs a bytevector input port (R7RS §6.13.2 binary). Same slot set as NewBinaryInputPortFromReader; the kind tag distinguishes them.

func NewByteVectorOutputPortFromWriter

func NewByteVectorOutputPortFromWriter(wrt io.Writer) *PortObject

NewByteVectorOutputPortFromWriter buffers wrt and constructs a bytevector output port. ws is nil (binary). flsh is non-nil so Close flushes before closing.

func NewCharacterInputPort

func NewCharacterInputPort(rdr *bufio.Reader) *PortObject

NewCharacterInputPort wraps an existing *bufio.Reader as a textual input port (R7RS §6.13.2 textual port).

func NewCharacterInputPortFromReader

func NewCharacterInputPortFromReader(rdr io.Reader) *PortObject

NewCharacterInputPortFromReader buffers rdr and constructs a textual input port.

func NewCharacterOutputPortFromWriter

func NewCharacterOutputPortFromWriter(wrt io.Writer) *PortObject

NewCharacterOutputPortFromWriter buffers wrt and constructs a textual output port. wb is intentionally nil — R7RS textual ports do not expose byte-level writes even though *bufio.Writer satisfies io.ByteWriter. flsh is non-nil so Close flushes before closing.

func NewStringInputPortWithBuffer

func NewStringInputPortWithBuffer(buffer *bytes.Buffer) *PortObject

NewStringInputPortWithBuffer wraps a *bytes.Buffer as a string input port (R7RS §6.13.2 textual). bytes.Buffer satisfies io.Reader, io.RuneReader, and RuneUnreader directly — no buffering layer.

func NewStringInputPortWithReaders

func NewStringInputPortWithReaders(rdr io.Reader, rr io.RuneReader, urr RuneUnreader) *PortObject

NewStringInputPortWithReaders constructs a string input port whose rune reader and rune unreader are supplied externally. Used by fault-injecting test infrastructure (e.g., internal/extensions/ iotest) that needs to override read/unread semantics while still producing a *PortObject that production code's type assertions accept. Kind is portKindStringInput — caller is responsible for passing a reader whose semantics match a string-input port.

rdr provides the byte-level Read; rr and urr provide rune-level. They are wrapped in separate guarded* wrappers, so callers must pass consistent semantics (typically rdr and rr both wrap the same underlying *bytes.Buffer-equivalent source).

If rdr also implements io.Closer, Close is propagated. validateOrPanic runs as for any factory.

func NewStringOutputPort

func NewStringOutputPort() *PortObject

NewStringOutputPort creates a fresh string output port backed by an in-memory buffer. flsh is nil (no real flush needed); sext exposes the accumulated string via StringContent.

func NewStringOutputPortWithBuffer

func NewStringOutputPortWithBuffer(buffer *bytes.Buffer) *PortObject

NewStringOutputPortWithBuffer wraps the given buffer as a string output port. wb is intentionally nil — R7RS textual ports do not expose byte-level writes even though *bytes.Buffer satisfies io.ByteWriter. flsh is nil (no real flush needed).

func (*PortObject) AsByteReader

func (p *PortObject) AsByteReader() (io.ByteReader, bool)

AsByteReader returns the port's io.ByteReader and true if byte-readable; nil and false otherwise. Nil-safe.

func (*PortObject) AsByteUnreader

func (p *PortObject) AsByteUnreader() (ByteUnreader, bool)

AsByteUnreader returns the port's ByteUnreader and true if byte-unreadable; nil and false otherwise. Nil-safe.

func (*PortObject) AsByteVectorExtractor

func (p *PortObject) AsByteVectorExtractor() (ByteVectorExtractor, bool)

AsByteVectorExtractor returns the port's ByteVectorExtractor and true if extractable as bytevector; nil and false otherwise. Nil-safe.

func (*PortObject) AsByteWriter

func (p *PortObject) AsByteWriter() (io.ByteWriter, bool)

AsByteWriter returns the port's io.ByteWriter and true if byte-writable; nil and false otherwise. Nil-safe.

func (*PortObject) AsFlusher

func (p *PortObject) AsFlusher() (Flusher, bool)

AsFlusher returns the port's Flusher and true if a real flush is needed before close; nil and false otherwise. Nil-safe.

func (*PortObject) AsReader

func (p *PortObject) AsReader() (io.Reader, bool)

AsReader returns the port's io.Reader and true if readable; nil and false otherwise. Nil-safe.

func (*PortObject) AsRuneReader

func (p *PortObject) AsRuneReader() (io.RuneReader, bool)

AsRuneReader returns the port's io.RuneReader and true if rune-readable; nil and false otherwise. Nil-safe.

func (*PortObject) AsRuneUnreader

func (p *PortObject) AsRuneUnreader() (RuneUnreader, bool)

AsRuneUnreader returns the port's RuneUnreader and true if rune-unreadable; nil and false otherwise. Nil-safe.

func (*PortObject) AsRuneWriter

func (p *PortObject) AsRuneWriter() (RuneWriter, bool)

AsRuneWriter returns the port's RuneWriter and true if rune-writable; nil and false otherwise. Nil-safe.

func (*PortObject) AsStringWriter

func (p *PortObject) AsStringWriter() (io.StringWriter, bool)

AsStringWriter returns the port's io.StringWriter and true if string-writable; nil and false otherwise. Nil-safe.

func (*PortObject) AsWriter

func (p *PortObject) AsWriter() (io.Writer, bool)

AsWriter returns the port's io.Writer and true if writable; nil and false otherwise. Nil-safe.

func (*PortObject) Close

func (p *PortObject) Close() error

Close flushes (if a flusher is present) and closes the port. Close is idempotent — subsequent calls are no-ops. Nil-safe: a nil receiver returns nil.

func (*PortObject) EqualTo

func (p *PortObject) EqualTo(v Value) bool

EqualTo returns true iff v is a port with the same kind and datum identity. Nil-safe: a nil receiver compares equal only to nil.

func (*PortObject) IsClosed

func (p *PortObject) IsClosed() bool

IsClosed returns true if the port has been closed. Nil-safe: a nil receiver is treated as closed.

func (*PortObject) IsVoid

func (p *PortObject) IsVoid() bool

IsVoid returns true if the receiver is nil. Mirrors the void-receiver convention used by other value types.

func (*PortObject) PortKind

func (p *PortObject) PortKind() string

PortKind returns the Scheme-visible port kind tag (e.g., "binary-input-port"). Returns "" for a nil receiver — there is no "unknown" port kind in the codebase; the empty string is the nil-safe sentinel.

func (*PortObject) SchemeString

func (p *PortObject) SchemeString() string

SchemeString returns the Scheme external representation. Preserves the existing portBase format `<{kind} 0xADDR>` verbatim. A nil receiver returns `<port nil>`.

func (*PortObject) StringContent

func (p *PortObject) StringContent() (string, bool)

StringContent returns the accumulated string for string-output ports. Returns ("", false) if the port is not string-extractable. Nil-safe.

API asymmetry with AsByteVectorExtractor: this returns the resolved string directly while AsByteVectorExtractor returns the extractor interface for the caller to invoke. The asymmetry is a deliberate deferral — converging the two extractor APIs (either both returning the interface, or both returning the resolved value) is tracked as a follow-up in memory/2026-05-14-port-unification-impl.local.md under "Deferred follow-ups".

func (*PortObject) Validate

func (p *PortObject) Validate() error

Validate checks the cross-slot capability invariants I1–I7. Every New*Port factory calls Validate and panics on failure; embedders constructing PortObject literally may call this themselves.

Invariants:

  • I1: rb != nil requires rdr != nil
  • I2: rr != nil requires rdr != nil
  • I3: bidirectional pairing — rb requires urb (and vice versa); rr requires urr (and vice versa). Every factory in port_constructors.go assigns these slots together; tightening Validate to enforce both directions turns the construction convention into a checked invariant.
  • I4: wb, wr, ws non-nil require wrt != nil
  • I5: ext != nil requires wrt != nil
  • I6: sext != nil requires wrt != nil
  • I7: ext and sext are mutually exclusive

I8 (kind matches capability profile) is enforced by construction — every factory writes both kind and the slot set in the same function body, adjacent to one another — and is asserted at the per-factory test level.

type Process

type Process struct {
	// contains filtered or unexported fields
}

Process represents a running OS process. Wraps *exec.Cmd and its connected pipes. Accessors return the ports for stdout, stderr, and stdin.

func NewProcess

func NewProcess(
	command string,
	cmd *exec.Cmd,
	stdin *PortObject,
	stdout *PortObject,
	stderr *PortObject,
) *Process

NewProcess creates a Process value. The cmd may be nil for testing. Ports may be nil if the process was not started with pipes.

func (*Process) Cmd

func (p *Process) Cmd() *exec.Cmd

Cmd returns the underlying *exec.Cmd.

func (*Process) Command

func (p *Process) Command() string

Command returns the command name.

func (*Process) EqualTo

func (p *Process) EqualTo(v Value) bool

EqualTo returns true only for identity (same pointer).

func (*Process) IsVoid

func (p *Process) IsVoid() bool

IsVoid reports whether this process value is void. A nil *Process is considered void to satisfy the values.Value contract.

func (*Process) SchemeString

func (p *Process) SchemeString() string

SchemeString returns the Scheme external representation.

func (*Process) Stderr

func (p *Process) Stderr() *PortObject

Stderr returns the input port connected to the process stderr.

func (*Process) Stdin

func (p *Process) Stdin() *PortObject

Stdin returns the output port connected to the process stdin.

func (*Process) Stdout

func (p *Process) Stdout() *PortObject

Stdout returns the input port connected to the process stdout.

type Promise

type Promise struct {
	// contains filtered or unexported fields
}

Promise represents a delayed computation (R7RS lazy evaluation). A promise contains either an unevaluated thunk or a cached result.

R7RS §4.2.5: The first time a promise is forced, its body is evaluated and the result is memoized; on subsequent forces, the memoized result is returned.

func NewForcedPromise

func NewForcedPromise(value Value) *Promise

NewForcedPromise creates an already-forced promise with the given value. This is used by make-promise when given a non-promise value.

func NewPromise

func NewPromise(thunk Callable) *Promise

NewPromise creates a new unforced promise with the given thunk. The thunk should be a procedure that takes no arguments.

func (*Promise) CachedResult

func (p *Promise) CachedResult() Value

CachedResult returns the memoized result of a forced promise. Only valid when IsForced returns true.

func (*Promise) EqualTo

func (p *Promise) EqualTo(v Value) bool

EqualTo returns true if the promises are the same object.

func (*Promise) Force

func (p *Promise) Force(result Value)

Force transitions the promise from unforced to forced, caching the given result and clearing the thunk. Subsequent calls to IsForced return true and CachedResult returns the cached value.

func (*Promise) IsForced

func (p *Promise) IsForced() bool

IsForced reports whether the promise has been forced. A forced promise has a cached result and no thunk.

func (*Promise) IsVoid

func (p *Promise) IsVoid() bool

IsVoid returns true if the promise is nil.

func (*Promise) SchemeString

func (p *Promise) SchemeString() string

SchemeString returns the Scheme representation of the promise.

func (*Promise) Thunk

func (p *Promise) Thunk() Callable

Thunk returns the unevaluated procedure. Returns nil when the promise has been forced.

type RWMutex

type RWMutex struct {
	// contains filtered or unexported fields
}

RWMutex is a readers-writer lock for Scheme, owned entirely by Wile rather than wrapping sync.RWMutex. The reason is cancellation: sync.RWMutex.Lock/RLock have no ctx-aware form, so a thread parked acquiring one cannot be unparked by thread-terminate! and stalls the VM's teardown. Here acquisition parks on a cond via waitOnCondCtx and transitions the state under the guard mutex, so it is atomic with cancellation: a cancelled acquirer wakes and returns WITHOUT acquiring — no phantom hold that a wrapping goroutine would leak. A thread that already HOLDS the lock is unaffected: the wait side is cancellable, the held side is never force-released (that would expose the guarded resource out of serialization order). See docs/concurrency/cancellation.md.

Writers are preferred: once a writer is waiting, new readers block, so a stream of readers cannot starve a writer. This differs slightly from sync.RWMutex's internal fairness and is a deliberate, self-contained choice.

func NewRWMutex

func NewRWMutex(name string) *RWMutex

NewRWMutex creates a new RWMutex

func (*RWMutex) EqualTo

func (p *RWMutex) EqualTo(v Value) bool

EqualTo returns true if the RWMutexes are the same object.

func (*RWMutex) ID

func (p *RWMutex) ID() uint64

ID returns the RWMutex's unique identifier

func (*RWMutex) IsVoid

func (p *RWMutex) IsVoid() bool

IsVoid returns true if the RWMutex is nil.

func (*RWMutex) LockContext added in v1.19.0

func (p *RWMutex) LockContext(ctx context.Context) bool

LockContext acquires the write lock, blocking until it is granted or ctx is cancelled. It reports true if the lock was acquired, false if ctx cancelled the wait first (in which case the lock is NOT held).

func (*RWMutex) Name

func (p *RWMutex) Name() string

Name returns the RWMutex's name

func (*RWMutex) RLockContext added in v1.19.0

func (p *RWMutex) RLockContext(ctx context.Context) bool

RLockContext acquires a read lock, blocking until it is granted or ctx is cancelled. It reports true if the lock was acquired, false if ctx cancelled the wait first. Readers block while a writer holds or is waiting (writer priority).

func (*RWMutex) RUnlock

func (p *RWMutex) RUnlock()

RUnlock releases a read lock

func (*RWMutex) SchemeString

func (p *RWMutex) SchemeString() string

SchemeString returns the Scheme representation of the RWMutex.

func (*RWMutex) TryLock

func (p *RWMutex) TryLock() bool

TryLock tries to acquire the write lock without blocking

func (*RWMutex) TryRLock

func (p *RWMutex) TryRLock() bool

TryRLock tries to acquire the read lock without blocking

func (*RWMutex) Unlock

func (p *RWMutex) Unlock()

Unlock releases the write lock

type Rational

type Rational struct {
	// contains filtered or unexported fields
}

Rational represents a Scheme rational number (exact fraction).

func NewRational

func NewRational(num, denom int64) *Rational

NewRational creates a new Rational from numerator and denominator. The fraction is automatically normalized (reduced to lowest terms).

func NewRationalFromBigInt

func NewRationalFromBigInt(num, denom *big.Int) *Rational

NewRationalFromBigInt creates a new Rational from big.Int numerator and denominator.

func NewRationalFromRat

func NewRationalFromRat(r *big.Rat) *Rational

NewRationalFromRat creates a Rational from an existing big.Rat.

func (*Rational) Abs

func (p *Rational) Abs() Number

Abs returns the absolute value of this rational.

func (*Rational) Add

func (p *Rational) Add(o Number) Number

Add returns the sum of this Rational and another number.

R7RS §6.2.6: The + procedure returns the sum of its arguments. R7RS §6.2.2 Exactness: exact + exact = exact, exact + inexact = inexact.

func (*Rational) Denom

func (p *Rational) Denom() *big.Int

Denom returns the denominator as a big.Int.

func (*Rational) DenomInt64

func (p *Rational) DenomInt64() int64

DenomInt64 returns the denominator as int64 (may overflow for large values).

func (*Rational) Divide

func (p *Rational) Divide(o Number) (Number, error)

Divide returns the quotient of two numbers.

func (*Rational) EqualTo

func (p *Rational) EqualTo(v Value) bool

EqualTo implements R7RS equal? for Rational.

R7RS §6.1: equal? "returns the same as eqv? when applied to … numbers" — no latitude. So this delegates to EqvNumber (eqv.go), the single authority on numeric equivalence, rather than restating the rules. Restating them is what let equal? and eqv? drift apart on signed zero and on cross-representation inexacts.

func (*Rational) Float64Truncated

func (p *Rational) Float64Truncated() float64

Float64Truncated returns the rational as a float64, discarding the big.Rat.Float64() exact-bool signal. The name documents the silent loss (1/3 → 0.333..., 2^100 → 1.2e+30, 1e500 → +Inf). Callers that need the signal should use Float64WithAccuracy or, at the cross-package boundary, the values.ToFloat64WithAccuracy helper.

func (*Rational) Float64WithAccuracy

func (p *Rational) Float64WithAccuracy() (float64, big.Accuracy)

Float64WithAccuracy returns the rational as a float64 paired with a big.Accuracy direction. Returns big.Exact when the rational is exactly representable in float64, else big.Below/Above depending on rounding direction; ±Inf overflow is reported as Above/Below relative to the finite limit. See rationalToFloat64WithAccuracy for the registry-path equivalent.

func (*Rational) HashCode

func (p *Rational) HashCode() uint64

HashCode returns a hash of the rational value. Uses the canonical exact-family hash so that Integer, BigInteger, and Rational produce identical hashes for equal values.

func (*Rational) IsExact

func (p *Rational) IsExact() bool

IsExact returns true since Rational is always exact.

R7RS §6.2.2: Rationals are always exact numbers.

func (*Rational) IsFinite

func (p *Rational) IsFinite() bool

IsFinite returns true since exact rationals are always finite.

R7RS §6.2.6: finite? returns #t for all exact numbers.

func (*Rational) IsInteger

func (p *Rational) IsInteger() bool

IsInteger returns true if the rational represents an integer (denominator is 1).

func (*Rational) IsNaN

func (p *Rational) IsNaN() bool

IsNaN returns false since exact rationals are never NaN.

R7RS §6.2.6: nan? returns #f for exact numbers.

func (*Rational) IsNegative

func (p *Rational) IsNegative() bool

IsNegative returns true if this rational is negative.

func (*Rational) IsPositive

func (p *Rational) IsPositive() bool

IsPositive returns true if this rational is positive.

func (*Rational) IsRational

func (p *Rational) IsRational() bool

IsRational returns true since Rational is always a rational number.

R7RS §6.2.6: rational? returns #t for exact rationals.

func (*Rational) IsVoid

func (p *Rational) IsVoid() bool

IsVoid returns true if the rational is nil.

func (*Rational) IsZero

func (p *Rational) IsZero() bool

IsZero returns true if the rational equals zero.

func (*Rational) Kind

func (p *Rational) Kind() NumericKind

Kind returns the numeric kind for dispatch table indexing.

func (*Rational) LessThan

func (p *Rational) LessThan(o Number) bool

LessThan returns true if this rational is less than another number.

func (*Rational) Multiply

func (p *Rational) Multiply(o Number) Number

Multiply returns the product of two numbers.

func (*Rational) Negate

func (p *Rational) Negate() Number

Negate returns the negation of this rational.

R7RS §6.2.6: The - procedure with one argument returns the additive inverse.

func (*Rational) Num

func (p *Rational) Num() *big.Int

Num returns the numerator as a big.Int.

func (*Rational) NumInt64

func (p *Rational) NumInt64() int64

NumInt64 returns the numerator as int64 (may overflow for large values).

func (*Rational) Rat

func (p *Rational) Rat() *big.Rat

Rat returns the underlying big.Rat value.

func (*Rational) SchemeString

func (p *Rational) SchemeString() string

SchemeString returns the Scheme representation of the rational.

func (*Rational) Sign

func (p *Rational) Sign() int

Sign returns -1 if negative, 0 if zero, or 1 if positive.

func (*Rational) SignBit added in v1.19.0

func (p *Rational) SignBit() bool

SignBit reports whether this rational carries a negative sign bit.

Rational is exact, so it has no signed zero and this coincides with IsNegative.

func (*Rational) Subtract

func (p *Rational) Subtract(o Number) Number

Subtract returns the difference of two numbers.

R7RS §6.2.6: The - procedure returns the difference of its arguments. R7RS §6.2.2 Exactness: exact - exact = exact, exact - inexact = inexact.

func (*Rational) ToExact

func (p *Rational) ToExact() (Number, error)

ToExact returns this Rational unchanged since it is already exact.

R7RS §6.2.6: exact returns an exact representation of its argument.

func (*Rational) ToInexact

func (p *Rational) ToInexact() Number

ToInexact converts this Rational to an inexact Float. A rational too large for float64 becomes ±Inf, which is what Chez gives.

R7RS §6.2.6: inexact returns an inexact representation of its argument.

type RealNumber

type RealNumber interface {
	Number
	IsPositive() bool
	IsNegative() bool
	Sign() int

	// SignBit reports whether the value carries a negative sign bit.
	//
	// This is NOT IsNegative(), and the difference is the whole reason it exists.
	// IsNegative asks "is n < 0", which is FALSE for a negative zero: -0.0 is not
	// less than zero. IsPositive is false for it too, and Sign() returns 0 for BOTH
	// +0 and -0 -- so none of the three can see a negative zero at all.
	//
	// SignBit asks the IEEE question, and it is load-bearing wherever a zero's sign
	// picks a branch. (angle -0.0) is π, not 0, because -0.0 lies on the NEGATIVE
	// real axis. The trap is already documented at big_float.go ("Sign() returns 0
	// for ±0 ... but Signbit() sees it") -- and code kept falling into it, because
	// the predicate that sees it did not exist.
	//
	// For the exact kinds (Integer, BigInteger, Rational) there is no signed zero,
	// so SignBit coincides with IsNegative.
	SignBit() bool
}

RealNumber represents a real-valued number with sign operations.

R7RS §6.2.6: The positive? and negative? predicates apply only to real numbers. Sign returns -1, 0, or 1.

type Record

type Record struct {
	// contains filtered or unexported fields
}

Record represents a record instance as defined by R7RS define-record-type. Each record has a type descriptor and a slice of field values.

func NewRecord

func NewRecord(rt *RecordType, fields []Value) (*Record, error)

NewRecord creates a new Record with the given type and field values. Returns an error if rt is nil or len(fields) does not match rt.FieldCount().

func (*Record) EqualComponents added in v1.19.0

func (p *Record) EqualComponents(v Value, push func(a, b Value)) bool

EqualComponents pushes the two records' corresponding fields for Equal to compare, once the record types and field counts agree. A record field is mutable, so a record can hold itself; Equal's visited set closes the cycle that used to overflow the host stack here.

func (*Record) EqualTo

func (p *Record) EqualTo(v Value) bool

EqualTo implements structural equality for records. Two records are equal if they have the same type and all fields are equal.

func (*Record) Field

func (p *Record) Field(index int) Value

Field returns the value at the given field index, or nil if index is out of range.

func (*Record) FieldByName

func (p *Record) FieldByName(name *Symbol) Value

FieldByName returns the value of the field with the given name. Returns nil if the field is not found.

func (*Record) IsVoid

func (p *Record) IsVoid() bool

IsVoid returns true if the record is nil.

func (*Record) RecordType

func (p *Record) RecordType() *RecordType

RecordType returns the record's type descriptor.

func (*Record) SchemeString

func (p *Record) SchemeString() string

SchemeString returns the Scheme external representation of the record. Opaque records omit the "record:" prefix to avoid revealing their implementation.

func (*Record) SetField

func (p *Record) SetField(index int, value Value)

SetField sets the value at the given field index. Does nothing if index is out of range.

func (*Record) SetFieldByName

func (p *Record) SetFieldByName(name *Symbol, value Value)

SetFieldByName sets the value of the field with the given name. Does nothing if the field is not found.

type RecordType

type RecordType struct {
	// contains filtered or unexported fields
}

RecordType represents a record type descriptor as defined by R7RS define-record-type. It holds the type name, the ordered list of field names, and an optional parent type for record inheritance.

func NewDerivedRecordType

func NewDerivedRecordType(name *Symbol, parent *RecordType, fieldNames []*Symbol) *RecordType

NewDerivedRecordType creates a new RecordType that inherits from the given parent. If the parent is opaque, the derived type is also opaque.

func NewOpaqueRecordType

func NewOpaqueRecordType(name *Symbol, fieldNames []*Symbol) *RecordType

NewOpaqueRecordType creates a new RecordType that is opaque to generic inspection. Instances of opaque record types are not recognized by record? and cannot be inspected via record-type. Type-specific predicates and accessors still work. Panics if name is nil.

func NewRecordType

func NewRecordType(name *Symbol, fieldNames []*Symbol) *RecordType

NewRecordType creates a new RecordType with the given name and field names. The parent defaults to nil (no inheritance).

func (*RecordType) EqualTo

func (p *RecordType) EqualTo(v Value) bool

EqualTo implements identity-based equality for record types. Two record types are equal only if they are the same object.

func (*RecordType) FieldCount

func (p *RecordType) FieldCount() int

FieldCount returns the number of fields in this record type.

func (*RecordType) FieldIndex

func (p *RecordType) FieldIndex(name *Symbol) int

FieldIndex returns the index of the field with the given name, or -1 if not found.

func (*RecordType) FieldNames

func (p *RecordType) FieldNames() []*Symbol

FieldNames returns the ordered list of field name symbols.

func (*RecordType) IsOpaque

func (p *RecordType) IsOpaque() bool

IsOpaque returns true if this record type is opaque to generic inspection.

func (*RecordType) IsVoid

func (p *RecordType) IsVoid() bool

IsVoid returns true if the record type is nil.

func (*RecordType) Name

func (p *RecordType) Name() *Symbol

Name returns the record type's name symbol.

func (*RecordType) Parent

func (p *RecordType) Parent() *RecordType

Parent returns the parent record type, or nil if this is a base type.

func (*RecordType) SchemeString

func (p *RecordType) SchemeString() string

SchemeString returns the Scheme external representation of the record type. Opaque record types use #<type:N> to avoid revealing the record nature.

type RecordTypeConstraint

type RecordTypeConstraint struct {
	// contains filtered or unexported fields
}

RecordTypeConstraint validates that a value is a Record whose RecordType matches (or inherits from) a specific record type descriptor.

func NewRecordTypeConstraint

func NewRecordTypeConstraint(rtd *RecordType) *RecordTypeConstraint

NewRecordTypeConstraint creates a RecordTypeConstraint for the given record type descriptor. Panics if rtd is nil.

func (*RecordTypeConstraint) Check

func (p *RecordTypeConstraint) Check(v Value) (any, bool, error)

Check tests whether v is a Record whose type matches (or inherits from) the target record type descriptor. Walks the parent chain for subtype matching.

func (*RecordTypeConstraint) Description

func (p *RecordTypeConstraint) Description() string

Description returns a human-readable description of the record type constraint.

func (*RecordTypeConstraint) Name

func (p *RecordTypeConstraint) Name() string

Name returns the Scheme-facing name of the record type.

type RuneUnreader

type RuneUnreader interface {
	UnreadRune() error
}

RuneUnreader is the interface satisfied by readers that can unread the last rune. Mirrors io.RuneScanner's UnreadRune half.

type RuneWriter

type RuneWriter interface {
	WriteRune(rune) (int, error)
}

RuneWriter is the interface satisfied by writers that can write a rune. The stdlib has no equivalent; bufio.Writer and bytes.Buffer satisfy it.

type SchemeWriter

type SchemeWriter struct {
	// contains filtered or unexported fields
}

SchemeWriter provides cycle-aware writing of Scheme values. Two-pass datum label output (R7RS §2.4): pass 1 (findShared) traverses the value graph to identify multiply-referenced objects; pass 2 (write) emits #n= definitions on first encounter and #n# references thereafter. See BIBLIOGRAPHY.md "Two-Pass Datum Label Output".

Implementation note: Uses maps with concrete *Pair, *Vector, *Box and *Hashtable keys (not Tuple/Indexable interfaces) because: 1. Go map keys must be comparable types - interfaces are not suitable 2. Cycle/sharing detection requires pointer identity tracking 3. Each concrete type needs separate tracking for proper label assignment

func NewSchemeWriter

func NewSchemeWriter() *SchemeWriter

NewSchemeWriter creates a new SchemeWriter for cycle-aware output. Default mode is WriteModeWrite (labels only circular references).

func (*SchemeWriter) SetMaxDepth

func (p *SchemeWriter) SetMaxDepth(n int)

SetMaxDepth sets the maximum structural nesting depth the writer will descend before reporting ErrWriteDepthExceeded. A value of 0 means unlimited; negative values are clamped to 0. See DefaultMaxWriteDepth for the rationale. Mirrors the parser's SetMaxDepth.

func (*SchemeWriter) WriteString

func (p *SchemeWriter) WriteString(v Value) (string, error)

WriteString writes a Scheme value to a string with cycle detection. Circular and shared structures are represented using datum labels. It returns ErrWriteDepthExceeded if the value nests deeper than maxDepth (see DefaultMaxWriteDepth); on that error the returned string is empty.

type Scope

type Scope struct {

	// IsRebinding indicates whether this scope can potentially rebind auxiliary syntax.
	// True for let-syntax/letrec-syntax scopes which create local macro bindings.
	// False for with-binding-scope which only adds scopes for binding hygiene.
	// This distinction is used in (*SyntaxMatcher).literalScopesMatchWithChecker
	// (pkg/internal/match/syntax_adapter.go) to correctly handle auxiliary
	// syntax like => and else in cond/case.
	IsRebinding bool
	// Label is an optional human-readable description for debugging.
	// Examples: "lambda", "let-syntax", "intro:my-macro", "library:(wile kanren)".
	Label string
	// contains filtered or unexported fields
}

Scope is an identity marker for macro hygiene. Each macro invocation creates a fresh Scope. Hygiene checking uses pointer equality to determine if a binding's scopes are a subset of a reference's scopes. This implements Flatt's "sets of scopes" model where scopes are just unique tags, not environment hierarchies.

func AddScopeToSet

func AddScopeToSet(scopes []*Scope, newScope *Scope) []*Scope

AddScopeToSet adds a scope to a set if not already present

func FlipScopeInSet

func FlipScopeInSet(scopes []*Scope, target *Scope) []*Scope

FlipScopeInSet toggles the presence of a scope in a set. If the scope is present, it is removed; if absent, it is added. This is the core operation for syntax-local-introduce.

func NewRebindingScope

func NewRebindingScope() *Scope

NewRebindingScope creates a new scope that can potentially rebind auxiliary syntax. Used by let-syntax and letrec-syntax to mark scopes that could shadow literals.

func NewRebindingScopeWithLabel

func NewRebindingScopeWithLabel(label string) *Scope

NewRebindingScopeWithLabel creates a new rebinding scope with a label.

func NewScope

func NewScope() *Scope

NewScope creates a new scope with unique identity for hygiene tracking. By default, scopes are not rebinding scopes.

func NewScopeWithLabel

func NewScopeWithLabel(label string) *Scope

NewScopeWithLabel creates a new scope with a human-readable label for debugging. The label has no semantic effect — it is purely for diagnostics.

func RemoveScopeFromSet

func RemoveScopeFromSet(scopes []*Scope, target *Scope) []*Scope

RemoveScopeFromSet removes a scope from a set

func (*Scope) ID

func (p *Scope) ID() uint64

ID returns the unique identifier for this scope. This can be used as a macro application ID for tracing.

func (*Scope) String

func (p *Scope) String() string

String returns a human-readable representation of the scope. If a label is set, returns "scope:ID(label)"; otherwise "scope:ID".

type ScopeSet added in v1.19.0

type ScopeSet struct {
	// contains filtered or unexported fields
}

ScopeSet is a hygiene *query* constraint used when resolving a name to a binding. It is either the wildcard "all" — match any binding of the name, ignoring scopes and resolving by slot order — or a specific scope set, resolved hygienically per Flatt's model with the empty set included.

It collapses a state that was previously smeared across three carriers: a []*Scope slice whose nil value meant "match any" at some read sites and "the empty set" at others, plus a matchAny bool parameter and a scopeKeyed bool field bolted on where the slice could not carry the distinction. The three states — all / empty / specific — are now one value, so on the reference/query side the same nil no longer answers two opposite questions. (The binder-creation path in LocalEnvironmentFrame.MaybeCreateLocalBinding still reads a nil []*Scope as "match any"; that call is not yet converted.)

A binder's OWN scope set is not a ScopeSet: it is always a concrete []*Scope, because "all" is meaningless for identity. ScopeSet models the reference/query side only.

The zero value is the empty set — a specific, non-wildcard query — not the wildcard. "all" must be asked for explicitly via AllScopes, so a forgotten initialization can never silently widen a resolution, which is the whole point of the type.

func AllScopes added in v1.19.0

func AllScopes() ScopeSet

AllScopes returns the wildcard query: any binding of the name matches, resolved by slot order. This is the introspection / bare-symbol reflective read semantics — "any binding of this name" rather than "the empty scope set".

func EmptyScopes added in v1.19.0

func EmptyScopes() ScopeSet

EmptyScopes returns the ambient (empty) scope-set query: the constraint a reference written outside any macro expansion carries. It is ScopesOf(nil) under a name that states the intent, replacing the AmbientScopes() empty-slice sentinel.

func ScopesOf added in v1.19.0

func ScopesOf(scopes []*Scope) ScopeSet

ScopesOf returns a query constrained to the given scope set. A nil slice is the EMPTY set here (equivalent to EmptyScopes), NOT the wildcard — use AllScopes for that. This is the inverse of the historical footgun where a nil slice silently meant "match any".

func (ScopeSet) IsAll added in v1.19.0

func (q ScopeSet) IsAll() bool

IsAll reports whether this is the wildcard query.

func (ScopeSet) IsEmpty added in v1.19.0

func (q ScopeSet) IsEmpty() bool

IsEmpty reports whether this is the ambient (empty, non-wildcard) query.

func (ScopeSet) Scopes added in v1.19.0

func (q ScopeSet) Scopes() []*Scope

Scopes returns the underlying scope set for a specific or empty query. It is meaningless for AllScopes and returns nil there.

func (ScopeSet) String added in v1.19.0

func (q ScopeSet) String() string

String returns a debug representation: "all-scopes" for the wildcard, or "scopes{...}" with the sorted decimal scope IDs (empty for the empty set), reusing ScopeFingerprint so the format matches the map-key form.

type SourceContext

type SourceContext struct {
	Text   string
	File   string
	Start  SourceIndexes
	End    SourceIndexes
	Scopes []*Scope    // Scopes associated with this source location
	Origin *OriginInfo // Macro expansion origin chain (nil if not from macro)
}

SourceContext holds source location and hygiene information for a syntax object.

func NewSourceContext

func NewSourceContext(text, file string, start, end SourceIndexes) *SourceContext

NewSourceContext creates a new source context with the given location info.

func NewZeroValueSourceContext

func NewZeroValueSourceContext() *SourceContext

NewZeroValueSourceContext creates an empty source context.

func (*SourceContext) Clone

func (p *SourceContext) Clone() *SourceContext

Clone returns a shallow copy of the SourceContext. The Scopes slice and Origin pointer are shared with the original; callers that need to mutate those fields should assign new values after cloning (which is exactly what the With* methods do).

func (*SourceContext) EqualTo

func (p *SourceContext) EqualTo(value Value) bool

EqualTo returns true if this source context equals the given value.

func (*SourceContext) IsVoid

func (p *SourceContext) IsVoid() bool

IsVoid returns true if the source context is nil.

func (*SourceContext) Location

func (p *SourceContext) Location() string

Location returns the source location formatted as "file:line:col". Returns empty string if the receiver is nil or carries no location at all.

When File is empty (e.g. a nameless EvalMultiple program) but a position is present, the ":line:col" form is still returned so provenance is not lost. A truly position-less context (File=="" and Line==0) yields "", which is what lets machine.StackFrame.String fall through to the call site, or to the bare frame name, instead of printing ":0:0".

func (*SourceContext) SchemeString

func (p *SourceContext) SchemeString() string

SchemeString returns the Scheme representation of the source context.

func (*SourceContext) WithOrigin

func (p *SourceContext) WithOrigin(origin *OriginInfo) *SourceContext

WithOrigin returns a new SourceContext with the given origin chain. Used to attach macro expansion tracking information to syntax objects.

func (*SourceContext) WithScope

func (p *SourceContext) WithScope(scope *Scope) *SourceContext

WithScope returns a new SourceContext with an additional scope.

This is the primitive operation for adding hygiene scopes to syntax objects. In Flatt's "sets of scopes" model, each syntax object carries a set of scopes that identifies its binding context.

Design Decision: Scopes are stored in SourceContext rather than on individual syntax types. This treats scopes as source-location metadata, keeping the syntax types simpler and the scope management centralized.

The new scope is prepended to the list (most recent scope first). This doesn't affect the ScopesMatch algorithm, which uses set membership.

Returns a NEW SourceContext (immutable design for syntax objects).

func (*SourceContext) WithScopes

func (p *SourceContext) WithScopes(scopes []*Scope) *SourceContext

WithScopes returns a new SourceContext with additional scopes

func (*SourceContext) WithoutScopes

func (p *SourceContext) WithoutScopes() *SourceContext

WithoutScopes returns a new SourceContext with scopes cleared. Used when creating template identifiers that should not inherit use-site scopes during macro expansion (Flatt 2016 hygiene model).

type SourceIndexes

type SourceIndexes struct {
	// contains filtered or unexported fields
}

SourceIndexes tracks position within a source file (index, column, line).

func NewSourceIndexes

func NewSourceIndexes(index, column, line int) SourceIndexes

NewSourceIndexes creates a new SourceIndexes with the given position.

func (SourceIndexes) Column

func (p SourceIndexes) Column() int

Column returns the column number within the current line (0-based).

func (SourceIndexes) EqualTo

func (p SourceIndexes) EqualTo(o Value) bool

EqualTo returns true if the positions are equal.

func (*SourceIndexes) Inc

func (p *SourceIndexes) Inc(n int) int

Inc advances the position by n characters on the same line.

func (SourceIndexes) Index

func (p SourceIndexes) Index() int

Index returns the absolute byte position in the source.

func (SourceIndexes) IsVoid

func (p SourceIndexes) IsVoid() bool

IsVoid returns false; SourceIndexes is never void.

func (SourceIndexes) Line

func (p SourceIndexes) Line() int

Line returns the line number (1-based).

func (*SourceIndexes) NewLine

func (p *SourceIndexes) NewLine() int

NewLine updates column and line tracking for a newline character. The index should already have been advanced by Inc(n) before calling this.

func (SourceIndexes) SchemeString

func (p SourceIndexes) SchemeString() string

SchemeString returns a string representation of the position.

func (*SourceIndexes) Tab

func (p *SourceIndexes) Tab() int

Tab advances the column to the next 8-column tab stop, assuming the column still points AT the tab (i.e. has not been stepped past it). Do not call it after Inc(1) for the tab character: Inc advances the column too, so the stop is computed one column late and over-advances by a whole stop when the tab lands on a column congruent to 7 mod 8. See tokenizer.tabStop, which computes the stop itself rather than using this method.

type SourceLocation

type SourceLocation interface {
	Value
	Index() int
	Column() int
	Line() int
}

SourceLocation represents a position in source code.

type String

type String struct {
	Value string
	// contains filtered or unexported fields
}

String represents a Scheme string value. R7RS §6.7: Literal strings and strings from symbol->string are immutable.

func NewMutableString

func NewMutableString(str string) *String

NewMutableString returns a mutable String value. Use this for strings that may be mutated (e.g., via string-set! or string-fill!). R7RS §6.7: Procedures like string-copy return mutable strings.

func NewString

func NewString(str string) *String

NewString returns an immutable String value. R7RS §6.7: Literal strings and strings from symbol->string are immutable. Use NewMutableString for runtime-allocated strings that may be mutated.

func (*String) EqualTo

func (p *String) EqualTo(v Value) bool

EqualTo returns true if the strings have equal values.

func (*String) Fill

func (p *String) Fill(char rune, start, end int) error

Fill fills the string (or a portion of it) with the given character. Returns an error if the string is immutable. R7RS §6.7: (string-fill! string fill [start [end]])

func (*String) Get

func (p *String) Get(i int) Value

Get returns the character at the given rune index as a Character value.

R7RS §6.7: (string-ref string k) returns character k of string.

func (*String) HashCode

func (p *String) HashCode() uint64

HashCode returns a hash of the string value.

func (*String) IsImmutable

func (p *String) IsImmutable() bool

IsImmutable returns true if the string cannot be mutated. Literal strings and strings returned by symbol->string are immutable. R7RS §6.7: It is an error to apply mutation procedures to literal strings or strings returned by symbol->string.

func (*String) IsVoid

func (p *String) IsVoid() bool

IsVoid returns true if the string is nil.

func (*String) Len

func (p *String) Len() int

Len returns the length of the string in characters (runes).

func (*String) Length

func (p *String) Length() int

Length returns the length of the string in characters (runes).

R7RS §6.7: (string-length string) returns the number of characters.

func (*String) Runes

func (p *String) Runes() []rune

Runes returns the string as a slice of runes.

func (*String) SchemeString

func (p *String) SchemeString() string

SchemeString returns the Scheme representation of the string.

func (*String) Set

func (p *String) Set(i int, v Value) error

Set sets the character at the given rune index from a Character value. Returns an error if the string is immutable.

PANICS if v is not a *Character (the assertion is unchecked) or if i is out of range. Callers are expected to have type- and bounds-checked their arguments; the sibling (*ByteVector).Set does check, and returns a wrapped sentinel.

R7RS §6.7: (string-set! string k char) stores char in element k.

func (*String) SetChar

func (p *String) SetChar(k int, char rune) error

SetChar sets the character at index k to the given rune. Returns an error if the string is immutable.

R7RS §6.7: (string-set! string k char) R7RS §6.7: "It is an error" to mutate literal strings or strings returned by symbol->string. This implementation signals an error when mutation is attempted on immutable strings.

func (*String) SetValue

func (p *String) SetValue(s string) error

SetValue sets the entire string value. Returns an error if the string is immutable.

func (*String) String

func (p *String) String() string

type StringExtractor

type StringExtractor interface {
	String() string
}

StringExtractor is implemented by buffers that can yield their accumulated bytes as a string. *bytes.Buffer satisfies it. Symmetric with ByteVectorExtractor.

type StringSet added in v1.19.0

type StringSet map[string]struct{}

type Symbol

type Symbol struct {
	Key string
}

Symbol represents a Scheme symbol.

func BigAccuracyToSymbol

func BigAccuracyToSymbol(acc big.Accuracy) *Symbol

BigAccuracyToSymbol maps a Go big.Accuracy to the corresponding Scheme singleton symbol. Used by primitives in extensions/math/ that surface accuracy to Scheme.

func NewSymbol

func NewSymbol(key string) *Symbol

NewSymbol creates a new symbol with the given key.

func NewTemporaryVariableName

func NewTemporaryVariableName() *Symbol

NewTemporaryVariableName generates a unique symbol for use as a temporary variable. The symbol name has the format "__T_<base32-encoded-random-bytes>". Uses 128 bits of cryptographic randomness to ensure uniqueness. Thread-safe: uses crypto/rand which is safe for concurrent use. Panics if random number generation fails.

func (*Symbol) Copy

func (p *Symbol) Copy() Value

Copy returns a copy of the symbol.

func (*Symbol) EqualTo

func (p *Symbol) EqualTo(v Value) bool

EqualTo returns true if the symbols have equal keys.

func (*Symbol) HashCode

func (p *Symbol) HashCode() uint64

HashCode returns a hash of the symbol's key.

func (*Symbol) IsVoid

func (p *Symbol) IsVoid() bool

IsVoid returns true if the symbol is nil.

func (*Symbol) SchemeString

func (p *Symbol) SchemeString() string

SchemeString returns the R7RS external representation of the symbol.

R7RS §7.1.1: Identifiers that can be represented without bars are written bare. Otherwise, they are enclosed in vertical bars with only \ and | characters escaped.

type SyntaxBase

type SyntaxBase struct {
	// contains filtered or unexported fields
}

SyntaxBase provides a common SourceContext() implementation for concrete syntax types via Go struct embedding. It eliminates boilerplate SourceContext() methods across the syntax type set (SyntaxObject, SyntaxSymbol, SyntaxPair, SyntaxVector, SyntaxComment, SyntaxDatum*, SyntaxDirective).

The sourceContext field is unexported; construct via NewSyntaxBase.

Note: IsVoid() and UnwrapAll() cannot be provided here:

  • IsVoid() requires nil receiver checks, which don't work with embedding
  • UnwrapAll() needs access to the outer type (self), not the embedded struct

func NewSyntaxBase

func NewSyntaxBase(sc *SourceContext) SyntaxBase

NewSyntaxBase constructs a SyntaxBase carrying the given source context.

func (*SyntaxBase) SetSourceContext

func (p *SyntaxBase) SetSourceContext(sc *SourceContext)

SetSourceContext replaces the source context. Used by syntax-list builders that thread per-element source contexts through a chain of pairs after construction.

func (*SyntaxBase) SourceContext

func (p *SyntaxBase) SourceContext() *SourceContext

SourceContext returns the source context carried by the embedding type.

type SyntaxForEachFunc

type SyntaxForEachFunc func(ctx context.Context, i int, hasNext bool, v SyntaxValue) error

SyntaxForEachFunc is the callback type for iterating over syntax tuples.

type SyntaxTuple

type SyntaxTuple interface {
	Tuple
	SyntaxValue
	SyntaxCar() SyntaxValue
	SyntaxCdr() SyntaxValue
	AsSyntaxVector() *SyntaxVector
	SyntaxAppend(value SyntaxValue) SyntaxValue
	SyntaxForEach(ctx context.Context, fn SyntaxForEachFunc) (SyntaxValue, error)
}

SyntaxTuple is the interface for syntax lists (pairs and vectors).

type SyntaxValue

type SyntaxValue interface {
	Value
	SourceContext() *SourceContext
	Unwrap() Value
	UnwrapAll() Value
}

SyntaxValue is the interface for all syntax objects. It provides access to source context and unwrapping capabilities.

The interface is defined in package values (rather than in the syntax package) so that the empty-list singleton (values.EmptyList) can directly implement it. This collapses the historical duality between values.emptyListType and the (now removed) syntaxEmptyListType — the empty list carries no symbols, no scopes, and no source-attachable hygiene content, matching Chez's `(equal? (syntax ()) '()) → #t`.

var SyntaxVectorVoidValue SyntaxValue

SyntaxVectorVoidValue is the syntax-level void value, set by pkg/syntax at init time. SyntaxVector.SyntaxForEach with a nil receiver returns this value to preserve the original "syntax void tail" semantics (distinguishable from the empty-list tail).

MUST be non-nil. See SyntaxValueUnwrapAllFunc for the rationale.

type SyntaxVector

type SyntaxVector struct {
	Values []SyntaxValue
	SyntaxBase
}

SyntaxVector wraps a Scheme vector with source context.

The pair-side recursive scope-propagation logic for SyntaxVector lives in the pkg/syntax package alongside the other concrete syntax data types. Only the data type itself is here so the SyntaxTuple interface (which references *SyntaxVector via AsSyntaxVector) can also live in values.

func NewSyntaxVector

func NewSyntaxVector(sc *SourceContext, vs ...SyntaxValue) *SyntaxVector

NewSyntaxVector creates a new syntax vector with the given source context and elements.

func (*SyntaxVector) AddScope

func (p *SyntaxVector) AddScope(scope *Scope) SyntaxValue

AddScope recursively propagates a scope to all nested syntax values.

Implements scope propagation for Flatt's "sets of scopes" hygiene. When a macro expands, the intro scope must be added to all identifiers (symbols) in the expansion. Empty vectors return self unchanged.

Panics if SyntaxVectorAddScopeFunc is nil — see the var declaration for why a silent fallback would be unsafe.

func (*SyntaxVector) EqualTo

func (p *SyntaxVector) EqualTo(o Value) bool

EqualTo performs pointer comparison only, matching Chez Scheme/Racket behavior. Two syntax objects are equal? only if they are the same object. For value comparison of syntax objects, use bound-identifier=? or free-identifier=?.

func (*SyntaxVector) ForEach

func (p *SyntaxVector) ForEach(ctx context.Context, fn ForEachFunc) (Value, error)

ForEach iterates over the elements of the vector as regular values in index order. It provides tuple-style iteration compatible with values.ForEachFunc callbacks.

The context poll mirrors Pair.ForEach. It has no caller to serve today — this method has none, and *SyntaxVector is not a Tuple (no AsVector), so it never reaches ForEachProperList. It is here because the signature promises otherwise: a ForEach that accepts a ctx and never reads it is exactly the shape that let apply ignore cancellation for as long as it did, and the next caller to wire this up would inherit that bug rather than write it. No cycle hazard — a vector has no cdr chain to close — but length is unbounded, so cancellation must land.

func (*SyntaxVector) IsVoid

func (p *SyntaxVector) IsVoid() bool

IsVoid returns true if the syntax vector is nil.

func (*SyntaxVector) SchemeString

func (p *SyntaxVector) SchemeString() string

SchemeString returns the Scheme representation of the syntax vector.

func (*SyntaxVector) SyntaxForEach

func (p *SyntaxVector) SyntaxForEach(ctx context.Context, fn SyntaxForEachFunc) (SyntaxValue, error)

SyntaxForEach iterates over the syntax elements of the vector. A nil receiver returns the syntax-void singleton (semantically distinct from the empty-list tail returned for an iterated vector).

The callback is invoked for each element with its index and a boolean indicating whether there is another element after the current one. If the callback returns an error, iteration stops immediately and the error is returned.

Panics if SyntaxVectorVoidValue is nil — see the var declaration for why a silent fallback would be unsafe (returning the empty list instead of void changes the semantics callers branch on).

func (*SyntaxVector) Unwrap

func (p *SyntaxVector) Unwrap() Value

func (*SyntaxVector) UnwrapAll

func (p *SyntaxVector) UnwrapAll() Value

UnwrapAll recursively unwraps all elements to produce a plain values.Vector, using the syntax package's cycle-aware recursive unwrap.

Panics if SyntaxValueUnwrapAllFunc is nil — see the var declaration for why a silent fallback would be unsafe (cyclic syntax structures would stack-overflow on a non-cycle-aware fallback walk).

type TerminatedThreadException

type TerminatedThreadException struct {
	Thread *Thread
}

TerminatedThreadException is raised when joining a terminated thread

func (*TerminatedThreadException) Error

func (p *TerminatedThreadException) Error() string

type Thread

type Thread struct {

	// RunFunc is set by the machine package to actually run the thread
	// This avoids circular dependency between values and machine
	RunFunc func(ctx context.Context, thunk Callable) (Value, error)

	// CleanupFunc is injected by the machine package to run dynamic-wind
	// after thunks (UnwindTo(0)) on thread exit. Called on both normal exit
	// and forced termination.
	CleanupFunc func()
	// contains filtered or unexported fields
}

Thread represents a Scheme thread (SRFI-18)

func NewThread

func NewThread(thunk Callable, name string) *Thread

NewThread creates a new thread that will execute the given thunk.

func (*Thread) AbandonOwnedMutexes

func (p *Thread) AbandonOwnedMutexes()

AbandonOwnedMutexes marks all mutexes owned by this thread as abandoned. Called during thread termination to ensure waiting threads are notified.

func (*Thread) Context

func (p *Thread) Context() context.Context

Context returns the context associated with this thread. Returns nil if the thread has not been started.

Hazard: p.ctx is read without holding p.mu, while Start writes it under p.mu. Calling this concurrently with Start is a data race.

func (*Thread) Done

func (p *Thread) Done() <-chan struct{}

Done returns a channel that's closed when the thread terminates

func (*Thread) EqualTo

func (p *Thread) EqualTo(v Value) bool

EqualTo returns true if both threads are the same object.

func (*Thread) ID

func (p *Thread) ID() uint64

ID returns the thread's unique identifier

func (*Thread) IsVoid

func (p *Thread) IsVoid() bool

IsVoid returns true if this thread is nil.

func (*Thread) Join

func (p *Thread) Join(timeout *time.Duration) (Value, error)

Join waits for the thread to terminate with optional timeout Returns the thread's result or an error

func (*Thread) Name

func (p *Thread) Name() string

Name returns the thread's name

func (*Thread) SchemeString

func (p *Thread) SchemeString() string

SchemeString returns the Scheme representation of this thread.

Hazard: it formats p.state without holding p.mu, unlike every other reader of that field (State, StateSymbol, Sleep, setOutcome) and unlike the sibling Mutex.SchemeString. Displaying a running thread therefore races with its own state transitions.

func (*Thread) SetSpecific

func (p *Thread) SetSpecific(v Value)

SetSpecific sets the thread's specific field

func (*Thread) Sleep

func (p *Thread) Sleep(d time.Duration)

Sleep pauses the thread for the given duration

func (*Thread) Specific

func (p *Thread) Specific() Value

Specific returns the thread's specific field (thread-local storage)

func (*Thread) Start

func (p *Thread) Start(parentCtx context.Context) error

Start begins execution of the thread. The parentCtx is used as the parent for the thread's cancellable context, enabling cancellation propagation from the engine/caller while allowing independent termination via thread-terminate!.

func (*Thread) State

func (p *Thread) State() ThreadState

State returns the current state of the thread

func (*Thread) StateSymbol

func (p *Thread) StateSymbol() *Symbol

StateSymbol returns the state as a Scheme symbol. Returns package-level singletons rather than fresh symbols; see the doc comment on SymbolThreadNew. StateSymbol has no Scheme-level primitive today.

func (*Thread) Terminate

func (p *Thread) Terminate()

Terminate forcefully terminates the thread. Marks all owned mutexes as abandoned and cancels the thread's context. The deferred cleanup in the goroutine (dynamic-wind after thunks) will fire when the goroutine exits. However, AbandonOwnedMutexes is also called here directly because the goroutine may be blocked on a Go-level operation (e.g., sync.Cond.Wait) and won't exit immediately on context cancellation.

func (*Thread) TrackMutex

func (p *Thread) TrackMutex(m *Mutex)

TrackMutex adds a mutex to this thread's ownership tracking set. Called by mutex-lock! when a mutex is acquired with this thread as owner.

func (*Thread) UntrackMutex

func (p *Thread) UntrackMutex(m *Mutex)

UntrackMutex removes a mutex from this thread's ownership tracking set. Called by mutex-unlock! when a mutex is released.

func (*Thread) Yield

func (p *Thread) Yield()

Yield is a no-op placeholder. Yielding is done by the thread-yield! primitive, which calls runtime.Gosched directly; nothing calls this method.

type ThreadState

type ThreadState int

ThreadState represents the state of a thread

const (
	ThreadNew        ThreadState = iota // Created but not started
	ThreadRunnable                      // Running or ready to run
	ThreadBlocked                       // Waiting for mutex/cv/sleep
	ThreadTerminated                    // Finished execution
)

ThreadState constants.

func (ThreadState) String

func (p ThreadState) String() string

type Time

type Time struct {
	// contains filtered or unexported fields
}

Time represents a point in time (SRFI-18)

func CurrentTime

func CurrentTime() *Time

CurrentTime returns the current time

func NewTime

func NewTime(t time.Time) *Time

NewTime creates a new Time from a Go time.Time

func NewTimeFromSeconds

func NewTimeFromSeconds(seconds float64) *Time

NewTimeFromSeconds creates a Time from seconds since epoch

func (*Time) Add

func (p *Time) Add(d time.Duration) *Time

Add returns a new Time that is the given duration after this time

func (*Time) After

func (p *Time) After(other *Time) bool

After returns true if this time is after another

func (*Time) Before

func (p *Time) Before(other *Time) bool

Before returns true if this time is before another

func (*Time) DurationFromNow

func (p *Time) DurationFromNow() time.Duration

DurationFromNow returns the duration from now until this time.

func (*Time) EqualTo

func (p *Time) EqualTo(v Value) bool

EqualTo returns true if both times represent the same instant.

func (*Time) GoTime

func (p *Time) GoTime() time.Time

GoTime returns the underlying Go time.Time

func (*Time) IsVoid

func (p *Time) IsVoid() bool

IsVoid returns true if the time is nil.

func (*Time) SchemeString

func (p *Time) SchemeString() string

SchemeString returns the Scheme representation of the time.

func (*Time) Seconds

func (p *Time) Seconds() float64

Seconds returns the time as seconds since the epoch

func (*Time) Sub

func (p *Time) Sub(other *Time) time.Duration

Sub returns the duration between this time and another

type Tuple

type Tuple interface {
	Value
	// Car returns the first element of the pair (R7RS §6.4).
	Car() Value
	// Cdr returns the rest of the list after the first element (R7RS §6.4).
	Cdr() Value
	// ForEach calls fn for each element in order. Returns the tail value
	// (EmptyList for proper lists, the improper cdr otherwise).
	ForEach(ctx context.Context, fn ForEachFunc) (Value, error)
	// Length returns the number of elements. For improper lists, this
	// counts only the proper prefix.
	Length() int
	// AsVector converts the list to a Vector (R7RS §6.4 list->vector).
	AsVector() *Vector
	// IsList reports whether this is a proper list (R7RS §6.4 list?).
	// Uses Floyd's cycle detection (tortoise-and-hare).
	IsList() bool
	// IsEmptyList reports whether this is the empty list (R7RS §6.4 null?).
	IsEmptyList() bool
	// IsVoid reports whether this value is void (nil receiver handling).
	IsVoid() bool
}

Tuple represents the Scheme list protocol — any value that can be consumed as a sequence of car/cdr pairs.

R7RS §6.4: Lists are chains of pairs terminated by the empty list. Tuple captures the operations needed to traverse, measure, and convert list-shaped values without requiring a concrete *Pair type.

Implemented by: Pair, emptyListType (EmptyList singleton), and syntax.SyntaxPair (the syntax-phase pair, asserted in pkg/syntax).

IsVoid is listed explicitly because Pair uses a nil-receiver convention where (*Pair)(nil) represents void, and the method must be dispatched through the interface to handle that case.

func List

func List(os ...Value) Tuple

List constructs a proper list from the given values. Returns EmptyList if no arguments are provided. The resulting list has the values in the same order as the arguments.

Implementation note: Block-allocates all Pair cells in a single slice and links them via cdr pointers. Callers receive the Tuple interface.

func VectorToList

func VectorToList(vs *Vector) Tuple

VectorToList converts a Vector to a proper list preserving element order. Returns EmptyList for nil or void vectors.

type TypeConstraint

type TypeConstraint interface {
	// Name returns the Scheme-facing type name (e.g., "integer", "point").
	Name() string
	// Description returns a human-readable description.
	Description() string
	// Check tests whether v satisfies this constraint.
	// On success, returns the narrowed value and true.
	// On failure, returns nil, false, and an error describing the mismatch.
	Check(Value) (any, bool, error)
}

TypeConstraint describes a type expectation for documentation and validation. Built-in types are represented by ValueType constants. User-defined types (e.g., record types) implement this interface directly.

A nil TypeConstraint means "unspecified" (no type info declared). TypeAny means "explicitly accepts any value."

type UncaughtException added in v1.19.0

type UncaughtException struct {
	Reason Value
}

UncaughtException is the SRFI-18 uncaught-exception object. thread-join! raises it into the joining thread when the joined thread terminated by raising an exception it did not handle; uncaught-exception-reason recovers the original condition. Unlike its Go-error sibling UncaughtThreadException, this is a Scheme-visible values.Value, so it can be handed to machine.RaiseInPlace.

It is an opaque control-flow handle, not a structural container: identity is pointer identity across eq?, eqv?, AND equal?, matching the sibling SRFI-18 objects (Thread, Mutex, ConditionVariable). Comparing by pointer keeps it Go-comparable regardless of Reason's dynamic type, and — because it is not a DeepEqualer — avoids the host-stack overflow a structural EqualTo would hit on a Reason cycle (equal.go documents that hazard for non-DeepEqualer recursive types). Each thread-join! mints a fresh wrapper, so structural equality would buy nothing.

func NewUncaughtException added in v1.19.0

func NewUncaughtException(reason Value) *UncaughtException

NewUncaughtException wraps the original raised condition in an SRFI-18 uncaught-exception object.

func (*UncaughtException) EqualTo added in v1.19.0

func (p *UncaughtException) EqualTo(v Value) bool

EqualTo compares by pointer identity: an uncaught-exception is equal only to itself. See the type doc for why this is a handle, not a structural container.

func (*UncaughtException) IsVoid added in v1.19.0

func (p *UncaughtException) IsVoid() bool

IsVoid reports whether the receiver is nil, per the default Value convention.

func (*UncaughtException) SchemeString added in v1.19.0

func (p *UncaughtException) SchemeString() string

SchemeString renders the wrapper and, for a human reader, the reason it carries.

type UncaughtThreadException

type UncaughtThreadException struct {
	Reason error
}

UncaughtThreadException wraps an exception that wasn't caught in a thread

func (*UncaughtThreadException) Error

func (p *UncaughtThreadException) Error() string

func (*UncaughtThreadException) Unwrap

func (p *UncaughtThreadException) Unwrap() error

type Value

type Value interface {
	SchemeString() string
	IsVoid() bool
	EqualTo(Value) bool
}

Value is the base interface for all Scheme values.

Every runtime object in Wile implements Value. The three methods correspond to fundamental Scheme operations:

  • SchemeString returns the external representation (R7RS §6.13.3 write).
  • IsVoid reports whether this value represents the absence of a result (e.g., the return value of set! or display). A nil receiver must return true so that missing values are treated as void.
  • EqualTo implements structural equality (R7RS §6.1 equal?).

Implementors MUST be Go-comparable

This is a hard requirement of the contract, and it has no compile-time expression — the compiler will not stop you from breaking it.

R7RS §6.1 defines eq?/eqv? on aggregates as "denote the same location in the store." A Scheme object must therefore HAVE a location, and Wile spells that as Go pointer identity: EqIdentity (utils.go) special-cases *Symbol (compared by Key, since interning was removed) and otherwise falls through to a bare `a == b` on the interface. It backs eq?, memq, and assq — the hot path. Go panics with "comparing uncomparable type" when the dynamic type behind an interface is a slice, map, or func. Not an error return: a panic, in the embedder's process.

The RECEIVER, not the underlying type, decides. Vector is []Value and it is perfectly safe, because its methods take POINTER receivers — the dynamic type boxed into the interface is *Vector, which is a pointer and hence comparable. The mistake to avoid is a VALUE receiver on a slice-, map-, or func-backed type, which puts the naked slice into the interface:

type MyThing []Value        // with VALUE receivers: eq? panics on it
type MyThing struct{ … }    // with POINTER receivers: *MyThing is comparable, safe

Enforced MODULE-WIDE by TestValue_AllImplementorsAreGoComparable, which type-checks every implementing package with go/types and asserts types.Comparable on each of the ~130 implementors. There is no roster to keep up to date and nothing is exempt: add a non-comparable Value anywhere in the module and that test fails.

If your type is not a Scheme datum — a compiler or VM container that merely wanted SchemeString for diagnostics — do not implement Value at all. Having an equality method is not the same as being a Value; give it a concrete EqualTo(T) instead. machine.Operations and machine.MultipleValues are the worked examples.

ADDING A NEW VALUE TYPE requires at minimum:

  1. values/<type>.go — implement Value (SchemeString, IsVoid, EqualTo), with POINTER receivers unless the type is already comparable (an empty struct, a scalar)
  2. values/<type>_test.go — test the three Value methods + type-specific behavior
  3. allValueExemplars — add an exemplar (value_isvoid_convention_test.go); this is what enforces both the IsVoid convention and Go-comparability

Depending on the type's role, also update:

  1. registry/core/prim_predicates.go — if it needs a type predicate (e.g., box?)
  2. ffi.go — if it maps to/from Go types via RegisterFunc
  3. values/scheme_writer.go — if it has internal structure that can be shared/circular
  4. machine/machine_context_apply.go — if it is callable (implements Callable)
  5. machine/native_template.go — if it can appear as a compile-time literal
  6. values/value_type.go — if the type participates in the extension-API type vocabulary: add a ValueType constant + a typeInfos row (name/description) + a check assignment in init(), AND a row in goTypeToValueType so SchemeTypeName renders a Scheme name instead of leaking the Go type via %T. Types named in Scheme but without a ValueType counterpart (Record, Box, Promise) instead get an arm in SchemeTypeName's explicit switch.

If the new type has capability-conditional operations (e.g., optional read/write/seek surfaces), expose them via AsXxx() (T, bool) methods following the *PortObject pattern (values/port.go — AsReader, AsByteWriter, etc.). Document any new slot invariants in a Validate() method that constructors call.

For numeric types, see the more detailed guide in values/numeric_kind.go (11 items).

var EOFObject Value = eofType{}

EOFObject is the singleton EOF value.

var Void Value = voidType{}

Void is the singleton void value.

func ForEach

func ForEach(ctx context.Context, o Value, fn ForEachFunc) (Value, error)

ForEach iterates over a Tuple value, calling fn for each element. If the value is not a Tuple, returns the value unchanged with no error. The callback receives the element index, whether more elements follow, and the element value. Returns the tail of the tuple (EmptyList for proper lists) and any error from the callback.

func NthCons

func NthCons(lst Value, n int64, name string) (Value, error)

NthCons advances n cons cells along the cdr chain and returns the remaining list (or improper tail). It is the unified primitive behind list-ref (NthCons(...).Car()) and list-tail (NthCons(...)). Returns ErrIndexOutOfRange if n is negative or exceeds the list length.

At n=0 the input is returned unchanged, including for EmptyList — this matches R7RS semantics where (list-tail x 0) is x.

func Single

func Single(t Tuple) (Value, bool)

Single returns the sole element of a single-element Tuple, or false if the Tuple has zero or more than one element. This avoids ForEach and its closure allocation for the common case of 1-element rest-arg lists.

func StringOrFalse

func StringOrFalse(s string) Value

StringOrFalse returns a Scheme string if s is non-empty, or #f if empty. Follows the BoolToBoolean precedent for eliminating repeated if/else patterns.

func UnconsTyped

func UnconsTyped[T any](v Value, headSentinel error, name, role string) (T, Value, error)

UnconsTyped is Uncons followed by a type assertion on the head. On head-type mismatch, returns a wrapped headSentinel with the expected-type phrase read via werr.TypeNameOf.

func ValueOrVoid

func ValueOrVoid(v Value) Value

ValueOrVoid returns v, or the singleton Void when v is nil. It collapses the repeated "nil accessor result -> Void" guard used by primitives whose Go accessor returns a nil Value for an unset slot (thread/mutex/condvar -specific, atomic-box load/swap). Follows the BoolToBoolean / StringOrFalse precedent for eliminating repeated if/else patterns.

type ValueType

type ValueType uint8

ValueType represents a Scheme type constraint for extension API contracts. Each constant maps to either a concrete Go type or an interface in the values package.

const (
	TypeAny               ValueType = iota // any Value
	TypeVoid                               // void singleton
	TypeBoolean                            // *Boolean
	TypeNumber                             // Number interface
	TypeComplex                            // ComplexNumber interface
	TypeReal                               // RealNumber interface
	TypeRational                           // *Rational
	TypeInteger                            // *Integer | *BigInteger (all Wile integers are exact)
	TypeFlonum                             // *Float | *BigFloat
	TypeString                             // *String
	TypeCharacter                          // *Character
	TypeSymbol                             // *Symbol
	TypeByte                               // *Byte
	TypePair                               // *Pair
	TypeList                               // Tuple interface
	TypeVector                             // *Vector
	TypeByteVector                         // *ByteVector
	TypeHashtable                          // *Hashtable
	TypeProcedure                          // Callable interface
	TypePort                               // *PortObject (Port marker interface)
	TypeInputPort                          // *PortObject with rdr slot non-nil
	TypeOutputPort                         // *PortObject with wrt slot non-nil
	TypeTextualInputPort                   // *PortObject with rr slot non-nil
	TypeTextualOutputPort                  // *PortObject with wr slot non-nil
	TypeBinaryInputPort                    // *PortObject with rb slot non-nil
	TypeBinaryOutputPort                   // *PortObject with wb slot non-nil
	TypeCount                              // sentinel — must be last
)

func (ValueType) Check

func (p ValueType) Check(v Value) (any, bool, error)

Check tests whether v satisfies this type constraint. On success, returns the narrowed value and true. On failure, returns nil, false, and an error describing the mismatch.

func (ValueType) Description

func (p ValueType) Description() string

Description returns a human-readable description of the type constraint.

func (ValueType) Name

func (p ValueType) Name() string

Name returns the Scheme-facing type name, satisfying the TypeConstraint interface.

func (ValueType) String

func (p ValueType) String() string

String returns the Scheme-style name for the type (e.g., "integer", "pair").

type Vector

type Vector []Value

Vector represents an R7RS vector, a fixed-size mutable array of values. Vectors are written as #(element ...) in Scheme syntax. Unlike lists, vectors provide O(1) access to elements by index.

func NewVector

func NewVector(vs ...Value) *Vector

NewVector creates a new Vector from the given values. Returns an empty vector if no arguments are provided.

func NewVectorWithLength

func NewVectorWithLength(length int) *Vector

NewVectorWithLength creates a new Vector of the given length. Every element is the nil Value (not Void); the caller is expected to fill all slots before the vector escapes.

func (*Vector) AsList

func (p *Vector) AsList() Tuple

AsList converts the vector to a proper list (linked list of pairs). Returns void (nil Pair) if the vector is void. Returns EmptyList if the vector is empty. Otherwise returns a newly constructed list containing the vector's elements.

func (*Vector) EqualComponents added in v1.19.0

func (p *Vector) EqualComponents(v Value, push func(a, b Value)) bool

EqualComponents pushes the two vectors' corresponding elements for Equal to compare, once the lengths agree.

func (*Vector) EqualTo

func (p *Vector) EqualTo(v Value) bool

EqualTo implements structural equality for vectors. Two vectors are equal if they have the same length and all corresponding elements are equal. Returns false if the other value is not a Vector.

The comparison is delegated to Equal, whose traversal is ITERATIVE: elements are drained from a heap worklist, not by recursive EqualTo calls, so a deeply nested or cyclic vector cannot overflow the Go stack. EqualComponents below is this type's whole contribution to it.

func (*Vector) Get

func (p *Vector) Get(i int) Value

Get returns the element at the specified index.

func (*Vector) IsVoid

func (p *Vector) IsVoid() bool

IsVoid returns true if the vector is a nil pointer. A nil vector represents the absence of a value, distinct from an empty vector.

func (*Vector) Length

func (p *Vector) Length() int

Length returns the number of elements in the vector. Returns 0 if the vector is void (nil pointer).

func (*Vector) SchemeString

func (p *Vector) SchemeString() string

SchemeString returns the Scheme external representation of the vector. Format: #( element1 element2 ... ) with elements separated by spaces. Empty vectors are represented as #(). Cyclic and cross-referential structures render a bounded "..." marker instead of overflowing the Go stack.

func (*Vector) Set

func (p *Vector) Set(i int, value Value) error

Set sets the element at the specified index to the given value. Vectors are always mutable, so this never returns an error.

type WriteMode

type WriteMode int

WriteMode controls how the SchemeWriter handles shared structure.

R7RS §6.13.3 specifies three output procedures with different sharing semantics:

  • write: datum labels only for circular references (WriteModeWrite)
  • write-shared: datum labels for all shared references (WriteModeWriteShared)
  • write-simple: no datum labels at all (handled separately via SchemeString)
const (
	// WriteModeWrite labels only circular references.
	// R7RS §6.13.3: write outputs datum labels only for objects that are part of a cycle.
	WriteModeWrite WriteMode = iota
	// WriteModeWriteShared labels all multiply-referenced objects.
	// R7RS §6.13.3: write-shared outputs datum labels for all shared structure.
	WriteModeWriteShared
)

Directories

Path Synopsis
Package valuestest provides test helpers for the values package.
Package valuestest provides test helpers for the values package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL