(rontolisp) docs
← Protocols, records and types

defrecord

(defrecord Name [fields...] Protocol (method [target & args] body...) ...)

Defines a record: a map with a type tag. The value wraps the entry table every map uses as (:C%RECORD tag fields table class), so the map verbs read through it (get/contains?/keys/vals/count/seq read the entries; assoc/update/ conj/merge rebuild the table and keep the tag; dissoc keeps the record while every declared field is still present and drops to a plain map otherwise, like the oracle). = compares two records by tag plus entries and never equals a plain map. Two constructors lower to mangled functions: ->Name positionally (a wrong count signals) and map->Name from a map (missing fields default to nil, extra entries kept); (Name. ...) rewrites to ->Name. Inline method bodies see the fields as locals, and an instance call reaches the inline methods and the declared fields (.name). The name joins the whole-file pre-scan, so a constructor call may stand above the definition.

A record prints as its literal, like the oracle: #user.R{:a 7} -- the defining namespace (- spelled _) plus the name, the declared fields first. The literal reads back, in source and through read-string/read: #ns.Name{:k v ...} builds the record over the unevaluated body (missing fields nil, extra keys kept), #ns.Name[v ...] positionally (a wrong count is refused). The class must be a record the program defines; an undotted #Name{...} is a tagged literal and is refused, like the oracle (No reader function for tag Name).

Deviation: str of a record spells its literal, where the oracle answers user.R@<hash>.