(sigil record)
(sigil record) - Dict-Backed Typed Values
Provides define-record for creating typed, extensible, immutable dict-backed values (the Clojure defrecord role). A record IS a real immutable dict — every dict operation works, arbitrary namespaced keys are allowed — yet it carries a unique type identity, declared fields (with optional defaults and type predicates), and single inheritance.
Records complement structs: define-struct is the opaque, fixed-slot, fast tier (the SRFI-9 / deftype role); define-record is the open data tier (the defrecord role). NOTE: this is deliberately NOT named define-record-type — SRFI-9 records are opaque and fixed, which in Sigil is define-struct.
Basic Usage
(import (sigil record))
(define-record buffer (name))
(define-record editor-buffer include: buffer (text default: "") (result))
(define e (editor-buffer name: "*editor*" text: "(+ 1 2)"))
(editor-buffer? e) ; => #t
(buffer? e) ; => #t (inherited)
(dict? e) ; => #t (it IS a dict)
(editor-buffer-text e) ; => "(+ 1 2)" (accessor)
(buffer-name e) ; => "*editor*" (parent accessor works)
(dict-ref e text:) ; => "(+ 1 2)" (dict ops work)Immutable Typed Update
Pass an existing record as the first constructor argument to get a functionally-updated copy that preserves the type:
(define e2 (editor-buffer e result: "3"))
(editor-buffer? e2) ; => #t
(editor-buffer-result e2) ; => "3"
(editor-buffer-result e) ; => #f (original untouched)Plain dict-set also preserves the type (the tag is a dict entry):
(editor-buffer? (dict-set e app/tag: "draft")) ; => #tThe Type Tag Is Visible (by design)
An instance is a dict tagged with its type descriptor under the reserved key sigil/record-type:. The tag being a visible entry means records serialize/datafy carrying their own type, and a plain dict is never a record. Treat sigil/record-type: as reserved.
Inheritance
include: gives single inheritance: the child type's instances satisfy the parent predicate, inherit the parent's field defaults, and multimethods dispatching on the parent type apply (see (sigil method)). Works across modules — the parent is referenced through its constructor.
Field Options
(define-record point
(x number? default: 0) ; type predicate + default
(y number? default: 0))Type predicates generate procedure specs for the accessors.
IMPLEMENTATION NOTE — descriptor is a hand-rolled tagged vector, NOT a struct: using define-struct here would be a cross-module procedural macro use INSIDE the stdlib build session, which pre-fix release binaries miscompile (the phantom-module bug this feature's task fixed; see test-module-phantom-instantiation.sgl). Keeping the stdlib free of internal cross-module procedural-macro uses preserves the ability to build the monorepo with one-release-old binaries.
Exports
make-record-typeprocedureCreate a record type descriptor.
fields is a list of field-name symbols; defaults an alist of (field-name . default-value); parent a record type or #f. Default values are evaluated by the CALLER (once, at type definition) — see the define-record notes on defaults.
(define point-type (make-record-type 'point '(x y) '((x . 0)) #f))record-type?procedureTest if a value is a record type descriptor.
(record-type? (record-type-of p)) ; => #trecord-type-nameprocedureGet the name of a record type as a symbol.
record-type-fieldsprocedureGet the record type's OWN declared field names (a list of symbols). Inherited fields are on the parent; walk record-type-parent.
record-type-defaultsprocedureGet the record type's OWN field defaults as an alist.
record-type-parentprocedureGet the parent record type, or #f if none.
record?procedureTest if a value is a record instance (a dict tagged with a record type descriptor under sigil/record-type:).
(record? (point x: 1)) ; => #t
(record? #{}) ; => #f (a plain dict is not a record)record-type-ofprocedureGet the record type descriptor of an instance, or #f.
record-isa?procedureTest if record type t is ancestor or inherits from it.
record-of?procedureTest if x is an instance of record type rt (or a subtype).
(record-of? e %editor-buffer--type) ; instance-of, chain-awarerecord-makeprocedureConstruct a record instance of type rt from a keyword-value plist, e.g. (record-make point-type (list x: 1 y: 2)). Declared field defaults (own + inherited) are filled first.
%record-constructprocedureInternal (referenced by define-record expansions): constructor body shared by all record constructors. If the first argument is already an instance of rt (or a subtype), copy-construct — apply the keyword updates functionally, preserving its type and any extra keys. A record/dict of the WRONG type as first argument is a clear error, not a keyword.
%record-ctor-typeprocedureInternal: extract the record type descriptor attached to a define-record constructor (used to resolve include: parents, including across modules).
define-recordvariable(No description)