DECLARE
Declares a new type in L4. Types can be records (product types), enums (sum types), or type synonyms.
Syntax
DECLARE TypeName IS ...
DECLARE TypeName HAS ...
DECLARE TypeName IS ONE OF ...
Forms
Record Types (Product Types)
Records are types with named fields:
DECLARE Person
HAS
name IS A STRING
age IS A NUMBER
Enum Types (Sum Types)
Enums define a type with multiple alternatives:
DECLARE Colour IS ONE OF red, green, blue
Enum constructors can have fields:
DECLARE Shape IS ONE OF
Circle HAS radius IS A NUMBER
Rectangle HAS width IS A NUMBER, height IS A NUMBER
Computed Fields (Methods)
Record fields can have a MEANS clause that defines a derived value — computed automatically from the record's other fields. These are analogous to methods, calculated properties, or derived attributes in other languages.
DECLARE Employee HAS
-- stored fields (primary attributes)
`name` IS A STRING
`date of birth` IS A NUMBER
`current year` IS A NUMBER
-- computed fields (derived attributes)
`age` IS A NUMBER
MEANS `current year` - `date of birth`
`adult` IS A BOOLEAN
MEANS `age` >= 18
Key points:
- Computed fields are accessed with
'sjust like stored fields:employee'sage`` - Computed fields may depend on other computed fields (chaining)
- Computed fields may call external functions using OF syntax:
MEANSfOFx,y`` - Computed fields may use WHERE and LET/IN for local bindings
- When constructing a record with WITH, only stored fields are supplied — computed fields are derived automatically
Referential transparency: Computed fields are pure — they may only reference sibling fields of the same record. There is no way to add a GIVEN parameter to a MEANS clause inside a DECLARE; the only input is the record itself. This is a deliberate design choice rooted in functional programming: a computed field is a total function from the record's stored state to a derived value, with no hidden dependencies on external state or arguments. In the language of the lambda calculus, each computed field is a closed term over the record's own bindings. This purity guarantee means that computed fields are referentially transparent — evaluating employee's \age`` will always yield the same result for the same record, regardless of when or where it is called. It also makes cycle detection decidable: the compiler builds a dependency graph over a finite set of sibling fields and rejects any strongly connected component, ensuring termination.
Style guide: Group stored fields first, then computed fields, so readers see the primary data before the derived logic. Depart from this convention when expository clarity calls for a different ordering — for instance, placing a computed field immediately after the stored fields it depends on.
Example file:
-- Computed Fields Example
-- Demonstrates "methods" on records: fields with MEANS clauses
-- whose values are automatically derived from other fields.
IMPORT prelude
-- ─────────────────────────────────────────────────────────
-- Basic computed fields (methods)
-- ─────────────────────────────────────────────────────────
-- Style: stored fields first, then computed/derived fields.
DECLARE Employee HAS
-- stored fields (primary attributes)
`name` IS A STRING
`date of birth` IS A NUMBER
`date of employment` IS A NUMBER
`monthly salary` IS A NUMBER
`current year` IS A NUMBER
-- computed fields (derived attributes / methods)
`age` IS A NUMBER
MEANS `current year` - `date of birth`
`years of service` IS A NUMBER
MEANS `current year` - `date of employment`
`senior` IS A BOOLEAN
MEANS `years of service` >= 10
`eligible` IS A BOOLEAN
MEANS `senior`
AND `monthly salary` <= 6000
-- Construction: only stored fields are supplied.
-- Computed fields are derived automatically.
veteran MEANS Employee WITH
`name` IS "Lim"
`date of birth` IS 1960
`date of employment` IS 1990
`monthly salary` IS 3000
`current year` IS 2026
-- Access computed fields with 's, just like stored fields.
#EVAL veteran's `age` -- 66
#EVAL veteran's `years of service` -- 36
#EVAL veteran's `senior` -- TRUE
#EVAL veteran's `eligible` -- TRUE
-- ─────────────────────────────────────────────────────────
-- Calling external functions from computed fields
-- ─────────────────────────────────────────────────────────
-- Multi-argument calls can use juxtaposition, OF, or mixfix:
-- `apply rate` `balance` `rate` -- juxtaposition
-- `apply rate` OF `balance`, `rate` -- OF (comma-separated)
-- `apply` `balance` `at rate` `rate` -- mixfix (natural language)
-- All are equivalent. Choose whichever reads best in context.
GIVEN `base` IS A NUMBER
`rate` IS A NUMBER
GIVETH A NUMBER
`apply rate` MEANS `base` * `rate` / 100
DECLARE Account HAS
`balance` IS A NUMBER
`rate` IS A NUMBER
`interest` IS A NUMBER
MEANS `apply rate` `balance` `rate`
`projected` IS A NUMBER
MEANS `balance` + `interest`
savings MEANS Account WITH
`balance` IS 10000
`rate` IS 5
#EVAL savings's `interest` -- 500.0
#EVAL savings's `projected` -- 10500.0
-- ─────────────────────────────────────────────────────────
-- WHERE and LET/IN inside computed fields
-- ─────────────────────────────────────────────────────────
GIVEN `x` IS A NUMBER
GIVETH A NUMBER
`square` MEANS `x` * `x`
DECLARE Shape HAS
`width` IS A NUMBER
`height` IS A NUMBER
`area` IS A NUMBER
MEANS `width` * `height`
`diag sq` IS A NUMBER
MEANS `result`
WHERE
`w2` MEANS `square` `width`
`h2` MEANS `square` `height`
`result` MEANS `w2` + `h2`
`scaled` IS A NUMBER
MEANS LET `factor` MEANS 2 IN `area` * `factor`
rect MEANS Shape WITH
`width` IS 3
`height` IS 4
#EVAL rect's `area` -- 12
#EVAL rect's `diag sq` -- 25
#EVAL rect's `scaled` -- 24
Type Synonyms
Create an alias for an existing type:
DECLARE Age IS NUMBER
DECLARE PersonName IS STRING
Examples
Example file:
-- DECLARE keyword examples
-- Record Types (Product Types)
-- Basic record with multiple fields
DECLARE Person
HAS
`full name` IS A STRING
age IS A NUMBER
-- Record with single line syntax
DECLARE Point HAS x IS A NUMBER, y IS A NUMBER
-- Enum Types (Sum Types)
-- Simple enum with value constructors
DECLARE Colour IS ONE OF `bright red`, `forest green`, `ocean blue`
-- Enum with constructors that have fields
DECLARE Shape IS ONE OF
Circle HAS radius IS A NUMBER
Rectangle HAS width IS A NUMBER, height IS A NUMBER
Point
-- Type Synonyms
DECLARE Age IS NUMBER
DECLARE PersonName IS STRING
-- Parameterized Types
GIVEN a IS A TYPE
DECLARE Box HAS contents IS AN a
GIVEN a IS A TYPE, b IS A TYPE
DECLARE MyPair HAS first IS AN a, second IS A b
-- Usage Examples
-- Create record instances
DECIDE `the applicant` IS Person WITH `full name` IS "John", age IS 30
DECIDE `the origin` IS Point WITH x IS 0, y IS 0
-- Use enum constructors
DECIDE `my favourite colour` IS `bright red`
DECIDE `my shape` IS Circle WITH radius IS 5
-- Use parameterized types
DECIDE `box with number` IS Box WITH contents IS 42
DECIDE `box with text` IS Box WITH contents IS "hello"
-- Evaluate examples
#EVAL `the applicant`'s `full name`
#EVAL `the applicant`'s age
#EVAL `my favourite colour`
Basic Record
DECLARE Customer
HAS
name IS A STRING
email IS A STRING
balance IS A NUMBER
Parameterized Types
Types can have type parameters:
GIVEN a IS A TYPE
DECLARE Box
HAS
contents IS AN a
Field Syntax Variations
L4 supports multiple syntaxes for fields:
-- Using IS A
DECLARE Person1 HAS name IS A STRING
-- Using colon
DECLARE Person2 HAS name: STRING
-- Using colon with article
DECLARE Person3 HAS name: A STRING
Related Keywords
- TYPE-KEYWORDS - Type-related keywords (IS, HAS, ONE OF)
See Also
- Types Reference - Complete type system documentation