# DECLARE

Declares a new type in L4. Types can be records (product types), enums (sum types), or type synonyms.

## Syntax

```l4
DECLARE TypeName IS ...
DECLARE TypeName HAS ...
DECLARE TypeName IS ONE OF ...
```

## Forms

### Record Types (Product Types)

Records are types with named fields:

```l4
DECLARE Person
  HAS
    name IS A STRING
    age IS A NUMBER
```

### Enum Types (Sum Types)

Enums define a type with multiple alternatives:

```l4
DECLARE Colour IS ONE OF red, green, blue
```

Enum constructors can have fields:

```l4
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.

```l4
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 `'s` just like stored fields: `employee's `age``
- Computed fields may depend on other computed fields (chaining)
- Computed fields may call external functions using OF syntax: `MEANS `f`OF`x`, `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:** 

```l4-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:

```l4
DECLARE Age IS NUMBER
DECLARE PersonName IS STRING
```

## Examples

**Example file:** 

```l4-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

```l4
DECLARE Customer
  HAS
    name IS A STRING
    email IS A STRING
    balance IS A NUMBER
```

### Parameterized Types

Types can have type parameters:

```l4
GIVEN a IS A TYPE
DECLARE Box
  HAS
    contents IS AN a
```

### Field Syntax Variations

L4 supports multiple syntaxes for fields:

```l4
-- 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](/l4/reference/types/keywords.md)** - Type-related keywords (IS, HAS, ONE OF)

## See Also

- **[Types Reference](/l4/reference/types.md)** - Complete type system documentation
