value

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ShowDefaultWidth = 100
	ShowDefaultDepth = 50
)

Variables

View Source
var EmptyTuple = Value{Tag: TupleTag, Ref: &Tuple{}}

EmptyTuple is the shared empty tuple / empty list. It is immutable, so a single instance is reused everywhere one is needed.

View Source
var Force func(v Value) Value

Force reduces v to weak head normal form.

View Source
var InitialBuiltins = map[string]*Builtin{}

InitialBuiltins maps each builtin name to its first-class Builtin value.

View Source
var PrimArity = [primOpCount]int{
	PrimAdd: 2, PrimSub: 2, PrimMul: 2, PrimDiv: 2, PrimFdiv: 2, PrimMod: 2, PrimFmod: 2, PrimPow: 2,
	PrimSqrt: 1,
	PrimEq:   2, PrimLt: 2, PrimLte: 2, PrimGte: 2, PrimGt: 2, PrimNeq: 2,
	PrimEqual: 2,
	PrimEval:  1, PrimPeek: 1, PrimShow: 1, PrimWrite: 1, PrimBwrite: 1, PrimString: 1,
	PrimHash: 1,
	PrimSeq:  2,
}

PrimArity gives each PrimOp's required number of arguments, indexed by the dense enum.

View Source
var PrimNames = [primOpCount]string{
	PrimAdd: "add", PrimSub: "sub", PrimMul: "mul", PrimDiv: "div",
	PrimFdiv: "fdiv", PrimMod: "mod", PrimFmod: "fmod", PrimPow: "pow",
	PrimSqrt: "sqrt",
	PrimEq:   "eq", PrimLt: "lt", PrimLte: "lte", PrimGte: "gte", PrimGt: "gt", PrimNeq: "neq",
	PrimEqual: "equal",
	PrimEval:  "eval", PrimPeek: "peek", PrimShow: "show", PrimWrite: "write", PrimBwrite: "bwrite", PrimString: "string",
	PrimHash: "hash",
	PrimSeq:  "seq",
}

PrimNames gives the source-level name of each PrimOp, indexed by the dense enum. Used in error messages and bytecode/IR dumps.

View Source
var RaiseBuiltinError func(message string)

RaiseBuiltinError aborts execution from inside a structural builtin, locating the error at the active builtin's application span and tracing the reduction stack that was live when the builtin was entered.

Functions

func DeepEqual

func DeepEqual(a, b Value, seen map[comparisonPair]bool) bool

DeepEqual compares two values by structure, forcing them as needed. It handles cyclic structures by tracking pairs of values it has already started comparing.

func PrimName

func PrimName(op PrimOp) string

PrimName returns the source-level name of a primitive operation.

func ShowConst

func ShowConst(v Value) string

ShowConst renders a compile-time constant Value without forcing anything. Core/bytecode constants are already fully built (numbers, prebuilt code-point lists for string literals, the empty tuple, builtins), so this walks them directly rather than routing through Force, keeping the dumps independent of the machine.

func StringifyValue

func StringifyValue(value Value) string

func StringifyValueFull

func StringifyValueFull(value Value) string

Types

type Apply

type Apply struct {
	Fn  Value
	Arg Value
	Pos source.SourcePos
}

An Apply is a runtime-synthesized application Fn Arg. The compiled bytecode never builds one — it pushes arguments and enters the head (the spine-less model). The only place an Apply is created is reducing a composition, which must turn (First ∘ Second) x into the application First (Second x). Pos locates a "cannot apply" failure.

type Builtin

type Builtin struct {
	Prim  PrimOp
	Arity int
	Args  []Value
	Name  string
}

A Builtin is a primitive operation as a first-class value. It carries the operation, its arity, and the arguments gathered so far; applying it appends one argument, and when the count reaches Arity the machine runs the operation. This single representation is both the saturated and the partially-applied form: partial application is just a Builtin with fewer Args than Arity (see prims.go).

type Closure

type Closure struct {
	Code    PC
	Env     []Value
	Frame   int
	NoMatch source.SourcePos
}

A Closure is a lambda (one or more pattern cases) paired with the minimal set of free variables it captured. Frame is the largest of its cases' frame sizes, allocated once per application. NoMatch is the source span of the whole pattern set, used to locate a "no pattern matched" error.

type Composition

type Composition struct {
	First  Value
	Second Value
}

A Composition is an unreduced function composition. Applying (First ∘ Second) to x reduces to First (Second x); see the CompositionTag case of reduce.

type Cons

type Cons struct {
	Head Value
	Tail Value
}

A Cons is a head/tail pair. Thunky draws no distinction between a 2-tuple and a list cons cell (see LANGUAGE.md §11), so every arity-2 tuple — list literals, string code points, the stdin stream, a bare [a, b] pair — is a Cons. This packs the pair into one allocation and lets list code destructure cons cells directly.

type ListEnding

type ListEnding int
const (
	NotAList ListEnding = iota
	ProperList
	Truncated
	Cyclic
)

type PC

type PC = int32

PC is a program counter: an index into the compiled instruction array. It lives here because Thunk and Closure store entry points; the backend re-exports it.

const NoCode PC = -1

NoCode marks a thunk that has no compiled body: a graph-style indirection thunk (a pattern binding) whose deferred computation is simply "reduce Value". A real code thunk has Code >= 0.

type PrimOp

type PrimOp uint8
const (
	PrimAdd PrimOp = iota
	PrimSub
	PrimMul
	PrimDiv
	PrimFdiv
	PrimMod
	PrimFmod
	PrimPow
	PrimSqrt
	PrimEq
	PrimLt
	PrimLte
	PrimGte
	PrimGt
	PrimNeq

	PrimEqual
	PrimEval
	PrimPeek
	PrimShow
	PrimWrite
	PrimBwrite
	PrimString
	PrimHash

	PrimSeq // seq a b: force a to WHNF, return b unforced (the only non-numeric,

)

type Tag

type Tag uint8
const (
	NumberTag      Tag = iota // Num holds the value; Ref is nil
	ConsTag                   // Ref = *Cons
	TupleTag                  // Ref = *Tuple   (arity 0, 1, 3, 4, …; never 2)
	ClosureTag                // Ref = *Closure
	BuiltinTag                // Ref = *Builtin
	CompositionTag            // Ref = *Composition
	ApplyTag                  // Ref = *Apply   (a runtime-synthesized application)
	ThunkTag                  // Ref = *Thunk   (not yet in weak head normal form)
)

type Thunk

type Thunk struct {
	Forced   bool
	Value    Value
	Name     string
	Code     PC
	Locals   []Value
	Upvalues []Value
	Read     func() Value
}

A Thunk is a deferred computation that memoises its result. It comes in three forms, distinguished without a tag field:

  • Read != nil — a stdin stream cell; forcing it calls Read to obtain its weak head normal form (a cons cell or the empty list) and reads one input item.
  • Code == NoCode — a graph indirection (a pattern binding); forcing it reduces the value already in Value and memoises the result. This keeps the bound name on the trace/show path without the name's owner having to re-evaluate.
  • Code >= 0 — a compiled thunk; forcing it runs the body at Code over the captured frames (whole-frame capture, see below).

Code thunks capture the entire enclosing activation (Locals and Upvalues) by reference and address it with the enclosing slot numbers — no renumbering, and mutual recursion in a let group resolves because every binding thunk shares the frame, which is fully populated before any binding is forced.

Every thunk memoises (call-by-need): the first force runs the computation and writes the weak-head-normal-form result back into Value (setting Forced), so it is never recomputed. This is why re-forcing a thunk that embeds an output builtin (peek/show/write/bwrite) does not repeat the effect — the effect runs exactly once, when the thunk is first forced.

type Tuple

type Tuple struct {
	Fields []Value
}

A Tuple is an ordered sequence of any arity except 2 (arity 0, 1, 3, 4, …); the empty tuple is also the empty list. Arity-2 tuples are Cons cells instead.

type Value

type Value struct {
	Tag Tag
	Num float64
	Ref any
}

A Value is one runtime value, passed around by value (not by pointer). It is a small tagged union: a Number lives inline in Num, while every compound or heap-resident value keeps a pointer in Ref. This is the deliberate alternative to a Go interface for the runtime representation:

  • A number never allocates. A float64 boxed into an interface{} heap-allocates on every arithmetic result; Value{Tag: NumberTag, Num: x} does not. Numeric programs allocate the most, so this is where the representation earns its keep.
  • Ref holds a *pointer* (to Cons, Tuple, …). A pointer stored in an interface does NOT allocate in Go, so compound values pay only their own heap cost, never an extra box.
  • The Tag makes the variant explicit and cheap to switch on, instead of a type switch on an interface's dynamic type.

func ApplyValue

func ApplyValue(fn, arg Value, pos source.SourcePos) Value

func BuiltinValue

func BuiltinValue(b *Builtin) Value

func ClosureValue

func ClosureValue(c *Closure) Value

func ConsValue

func ConsValue(h, t Value) Value

func EvalPrim

func EvalPrim(op PrimOp, args []Value) Value

EvalPrim executes a saturated numeric primitive. The machine forces every operand and verifies it is a number before calling (see finishBuiltin), so the kernels read .Num directly — re-checking the tag here would be pure duplication on the hot path.

Note the argument order: the comparison and subtraction builtins are threshold-first (`sub a b` is b - a, `lt a b` is b < a), so args[1] is the left operand of the arithmetic.

func EvalPrim1

func EvalPrim1(op PrimOp, a float64) Value

EvalPrim1 runs a unary numeric kernel on an already-forced operand.

func EvalPrim2

func EvalPrim2(op PrimOp, a, b float64) Value

EvalPrim2 runs a binary numeric kernel on already-forced operands, where a is the first source-level argument and b the second.

func EvalStructuralBuiltin

func EvalStructuralBuiltin(op PrimOp, args []Value) Value

EvalStructuralBuiltin executes structural primitives when saturated.

func FoldStringValue

func FoldStringValue(s string) Value

FoldStringValue decodes a string into the runtime representation of a string: a cons list of code points ending in the empty tuple. The lowerer calls it once per literal, building a shared immutable constant.

func FullNormalForm

func FullNormalForm(value Value, seen map[*Thunk]bool) Value

FullNormalForm forces a value and all its sub-values. It handles cycles by tracking Thunks it has already visited.

func NumberValue

func NumberValue(n float64) Value

func StdinBytes

func StdinBytes() Value

StdinBytes returns bstdin: the standard input as a lazy list of raw byte values.

func StdinCodePoints

func StdinCodePoints() Value

StdinCodePoints returns stdin: the standard input decoded as a lazy list of Unicode code points. A byte sequence that is not valid UTF-8 is a runtime error.

func ThunkValue

func ThunkValue(t *Thunk) Value

func TupleValue

func TupleValue(t *Tuple) Value

func (Value) Apply

func (v Value) Apply() *Apply

func (Value) Builtin

func (v Value) Builtin() *Builtin

func (Value) Closure

func (v Value) Closure() *Closure

func (Value) Composition

func (v Value) Composition() *Composition

func (Value) Cons

func (v Value) Cons() *Cons

func (Value) IsFunction

func (v Value) IsFunction() bool

IsFunction reports whether v can be applied to an argument. Used only for display and error wording.

func (Value) Thunk

func (v Value) Thunk() *Thunk

func (Value) Tuple

func (v Value) Tuple() *Tuple

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL