Use a record when several values describe one thing. Use an enum when a value is one of a fixed set of names. Use a closed choice when different alternatives carry different kinds of data. Use an optional when an otherwise ordinary value may be absent. These representations let the compiler check your assumptions.
Records
TYPE Customer
Name AS STRING
Visits AS INTEGER = 0
Notes AS LIST OF STRING = []
END TYPE
customer = Customer { Name="Ada" }
copy = customer
copy.Visits += 1
Append(BYREF copy.Notes, "Called today")
PRINT customer.Visits
PRINT LEN(customer.Notes)
PRINT copy.VisitsNamed construction supplies fields in any written order. Every field needs an
explicit value or a supported declared default. Explicit values evaluate in
written order, then omitted defaults in declaration order. Empty collection
defaults create independent collections for each record. Positional construction,
such as Customer("Ada"), follows field declaration order and may use trailing
defaults. Named function arguments are not a second spelling for record fields.
Ordinary copyable records compare their fields recursively. If a field owns a file or another resource, the containing record becomes noncopyable. A record with a cleanup hook also becomes a resource; chapter 10 explains that change.
Enums
ENUM Priority
Low
Normal
High = 10
END ENUM
priority = Priority.High
PRINT EnumValue(priority)
PRINT priority = Priority.High
PRINT Priority(10) = priorityMembers are qualified by the enum name. Values start at zero and increment unless
explicitly assigned. Enums are distinct static types, not aliases for INTEGER.
EnumValue extracts the number; construction from INTEGER checks membership and
can fail with NumericError. Duplicate values, aliases and implicit numeric
conversion are not provided.
Optionals
DIM nickname AS OPTIONAL OF STRING = NONE
IF LET text = nickname THEN
PRINT text
ELSE
PRINT "No nickname"
END IF
nickname = SOME("")
IF LET text = nickname THEN
PRINT "Present, length="; LEN(text)
END IFNONE means absence. SOME wraps a present value, even if that value is empty, zero
or FALSE. IF LET checks presence and introduces a branch-local independent binding
for a copyable payload. A borrowed stable optional supplies a read-only copy; a
fresh result or TAKE optional supplies a writable owner. You cannot access an absent payload. Nested optionals
distinguish outer absence from SOME(NONE), which is useful when a dictionary
contains an explicitly absent value or a task successfully returns NONE.
Resources have different IF LET ownership rules: a named optional loans its payload; a fresh or explicitly moved optional can provide an owned payload. Chapter 10 shows both.
Choose a fallback only when absent
optional ?? fallback unwraps one optional layer. A present value wins even if it
is zero, FALSE, empty text or an empty collection. Only NONE evaluates the fallback:
PRINT SOME(0) ?? DefaultNumber()
DIM absent AS OPTIONAL OF INTEGER = NONE
PRINT absent ?? DefaultNumber()
PRINT "[" + (SOME("") ?? "missing") + "]"
FUNCTION DefaultNumber() AS INTEGER
PRINT "Calculating fallback"
RETURN 99
END FUNCTIONOutput is 0, then Calculating fallback and 99, then empty brackets. This differs from dictionary Get with a fallback, whose ordinary argument evaluates eagerly. A fallback can fail through normal checked contracts; its contract is checked even when a particular execution skips it.
The operator associates to the right and binds below OR, AND, comparisons and
arithmetic. Use explicit parentheses when unwrapping nested optionals, such as
(outer ?? SOME(7)) ?? 9. A present outer SOME(NONE) is still present at the first
step. A fresh resource optional can donate its payload; a named resource optional
needs TAKE for ownership transfer. No implicit move or resource copy is introduced.
Borrowed copyable payloads produce independent values when ownership is needed.
See optional fallback and defaults
for parameter rules.
Closed choices
TYPE Reply
CASE Empty
CASE Text AS STRING
CASE Number AS INTEGER
END TYPE
reply = Reply { Text="Ready" }
SELECT TYPE reply
CASE Reply.Empty
PRINT "No reply"
CASE Reply.Text AS text
PRINT text
CASE Reply.Number AS number
PRINT number
END SELECTA choice holds exactly one declared alternative. SELECT TYPE checks the
alternatives, with independent case-local copyable payload bindings. Cover all
alternatives or provide the supported fallback form. Projecting a payload, such
as reply.Text, returns an optional; projecting a payload-free alternative returns
a Boolean. Alternative fields cannot be assigned directly: assign a complete
new choice instead.
Records and choices may form finite recursive values through supported lists, dictionaries and optionals. A direct inline containment cycle is invalid. Recursive value trees do not imply general shared reference graphs or cyclic back-references.
Record extension and opaque types
TYPE Person
Name AS STRING
END TYPE
TYPE Customer EXTENDS Person
Visits AS INTEGER = 0
END TYPE
customer = Customer { Name="Ada", Visits=2 }
ShowName(customer)
base = Person(customer)
PRINT base.Name
SUB ShowName(person AS Person)
PRINT person.Name
END SUBEXTENDS reuses fields and permits checked base access. It does not introduce
classes, virtual methods, downcasts or runtime subtype identity. Multiple bases
are allowed when layouts have no collisions or repeated ancestors. A default
base parameter borrows the actual value; Person(customer) explicitly constructs
an independent base value. BYREF base access preserves the derived fields and
cannot replace the complete base storage.
OPAQUE TYPE is a module boundary: clients can name and own values but cannot construct or extend the record or access hidden fields. Explicit PUBLIC fields are readable outside the defining module, including independently copying copyable fields. They do not grant external mutation, BYREF, SWAP or TAKE. Public field visibility is currently for ordinary opaque records; arbitrary member visibility on ordinary transparent records is not supplied. Chapter 12 shows the module pattern.