3.10 Things
Things are Heresy’s definable data structures. Unlike the objects of most object-oriented languages, which often exist to hold and carry mutable state and actions with which to change that state, Things are immutable. A Thing, once sprung to life, cannot itself be altered, but can be called with the correct syntax to return a new Thing with different internal values for its internal data fields.
Things are essentially functions, lambdas specifically, with predefined syntax arguments. They are first class values, and can be passed freely just as any other data, but can also be passed arguments to either return the values of their fields, return a new Thing, or to employ any functions contained within the thing.
Things can also optionally be given predicate types. Predicate typing is a form of typing in which types are defined by a predicate function, in other words a function which given a value, will return either true or false. In this way, any kind of type validation can be specified so long as it can be programmed as a function which returns a Boolean value. Things check their values against these types at both declaration, when the object is first described or instantiated, and at assignment of new values, ie. when the copy syntax is used to generate a new thing from the old one. If you attempt to describe or copy a thing whose values do not match its predicate types, the program will throw an error and indicate what field did not match its type.
syntax
(describe Name)
(describe Name (field [(type? args ...)] value) ...)
(describe Name extends super-thing (field [(type? args ...)] value) ...)
(describe Name extends super-thing inherit (id ...) (field [(type? args ...)] value) ...)
Optionally, after the field name, a type predicate can be provided. Type predicates are automatically "curried", ie. treated as a partial function with the initial arguments following the given type?, and expecting the result to be a single argument function that returns True or False. Things which are not given a type are automatically given the type any?, which returns True for any value.
If the extends option is provided, the new Thing extends super-thing, inheriting it’s fields and methods (unless they are overridden). If the inherit option is provided with it, then the ids are available as bindings within the method expressions. Typed things can be extended from untyped things and vice versa; the fields from the parent will inherit their types from the parent, unless overridden by creating a new field with the same name and a new type signature (or no signature, as the case may be). Note that parent things are never modified by their children.
> (describe Project (name "Destroy the world") (id 90) (budget 432000000)) > (Project 'budget) 432000000
> (describe Employee (name (string?) "Dave") (id (number?) 42) (dept (symbol?) 'it) (projects (list-of? number?) '(23 90 45))) > (Employee 'name) "Dave"
> (Employee '(* * "sales" *)) Thing encountered type error in assignment: dept must be
(symbol?)
> (def fn age-req? (age) (and (< 17 age) (> 45 age)))
> (describe Henchman extends Employee (weapon (symbol?) 'AK-47) (age (age-req?) 64)) Thing encountered type error in declaration: age must be
(age-req?)
syntax
(thing)
(thing (field [(type? args ...)] value) ...)
(thing extends super-thing (field [(type? args ...)] value) ...)
(thing extends super-thing inherit (id ...) (field [(type? args ...)] value) ...)
(Name)
(Name symbol) (Name 'fields) (Name alist) (Name pattern)
(Name)
(Name 'fields)
(Name symbol)
(Name alist)
alist = `(pair ...) pair = `(field value)
> (describe Beeb (model (symbol?) 'B) (ram (number?) 32) (cpu (string?) "m6502")) > (def BeebPlus (Beeb '((model B+) (ram 64)))) > (BeebPlus) '((model B+) (ram 64) (cpu "m6502"))
(Name pattern)
pattern = `(sub-pat ...) sub-pat = * | value
syntax
(Self ....)
syntax
syntax