Skip to content

Commit 9ad9d37

Browse files
authored
Merge pull request #847 from smucclaw/mengwong/methods
2 parents 14729c8 + 9972336 commit 9ad9d37

File tree

74 files changed

+2959
-80
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2959
-80
lines changed

doc/reference/GLOSSARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Keywords are reserved words that form the structure of L4 programs.
1919
| **GIVETH** / **GIVES** | Specifies function return type | [GIVETH](functions/GIVETH.md) |
2020
| **IN** | Used with LET for scoped bindings | [LET](functions/LET.md) |
2121
| **LET** | Introduces a local binding | [LET](functions/LET.md) |
22-
| **MEANS** | Defines the body of a function or decision | [MEANS](functions/MEANS.md) |
22+
| **MEANS** | Defines the body of a function, decision, or computed field (method) | [MEANS](functions/MEANS.md) |
2323
| **WHERE** | Introduces local declarations | [WHERE](functions/WHERE.md) |
2424
| **YIELD** | Creates anonymous functions (lambdas) | [YIELD](functions/YIELD.md) |
2525

@@ -66,7 +66,7 @@ Keywords are reserved words that form the structure of L4 programs.
6666
| **ASSUME** | Declares a variable of assumed type | [ASSUME](types/ASSUME.md) |
6767
| **DECLARE** | Defines a type | [DECLARE](types/DECLARE.md) |
6868
| **IS** | Type assertion or definition | [TYPE-KEYWORDS](types/keywords.md) |
69-
| **HAS** | Record field declaration | [TYPE-KEYWORDS](types/keywords.md) |
69+
| **HAS** | Record field declaration (supports computed fields / methods via MEANS) | [TYPE-KEYWORDS](types/keywords.md) |
7070
| **LIST** | List type or list literal | [TYPE-KEYWORDS](types/keywords.md) |
7171
| **ONE OF** | Used for enum types | [TYPE-KEYWORDS](types/keywords.md) |
7272
| **OF** | Type application or constructor pattern | [TYPE-KEYWORDS](types/keywords.md) |
@@ -208,6 +208,7 @@ For complete documentation, see **[Syntax Reference](syntax/README.md)**.
208208
| **Directives** | `#EVAL`, `#TRACE`, `#CHECK`, `#ASSERT` |
209209
| **Ditto** | `^` copy from previous line |
210210
| **Asyndetic** | `...` (AND) and `..` (OR) implicit operators |
211+
| **Computed Fields** | Derived attributes / methods via MEANS in HAS |
211212
| **Genitive** | `'s` for record field access |
212213
| **Section Markers** | `§` for document sections |
213214

doc/reference/functions/MEANS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ circumference radius MEANS
5454
pi MEANS 3.14159
5555
```
5656

57+
### In Record Fields (Computed Fields / Methods)
58+
59+
MEANS can also appear inside DECLARE HAS to define computed fields — derived attributes that are calculated automatically from other fields in the record:
60+
61+
```l4
62+
DECLARE Person HAS
63+
`birth year` IS A NUMBER
64+
`as at year` IS A NUMBER
65+
`age` IS A NUMBER
66+
MEANS `as at year` - `birth year`
67+
`adult` IS A BOOLEAN
68+
MEANS `age` >= 18
69+
```
70+
71+
The computed field's MEANS body can reference any sibling field by name. It can also call external functions (using OF for multi-argument calls), and use WHERE or LET/IN for local bindings.
72+
73+
See **[DECLARE - Computed Fields](../types/DECLARE.md#computed-fields-methods)** for full documentation.
74+
5775
## Alternative Forms
5876

5977
### Using IS

doc/reference/types/DECLARE.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,37 @@ DECLARE Shape IS ONE OF
3939
Rectangle HAS width IS A NUMBER, height IS A NUMBER
4040
```
4141

42+
### Computed Fields (Methods)
43+
44+
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.
45+
46+
```l4
47+
DECLARE Employee HAS
48+
-- stored fields (primary attributes)
49+
`name` IS A STRING
50+
`date of birth` IS A NUMBER
51+
`current year` IS A NUMBER
52+
-- computed fields (derived attributes)
53+
`age` IS A NUMBER
54+
MEANS `current year` - `date of birth`
55+
`adult` IS A BOOLEAN
56+
MEANS `age` >= 18
57+
```
58+
59+
**Key points:**
60+
61+
- Computed fields are accessed with `'s` just like stored fields: `employee's `age``
62+
- Computed fields may depend on other computed fields (chaining)
63+
- Computed fields may call external functions using OF syntax: `MEANS `f` OF `x`, `y``
64+
- Computed fields may use WHERE and LET/IN for local bindings
65+
- When constructing a record with WITH, only stored fields are supplied — computed fields are derived automatically
66+
67+
**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.
68+
69+
**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.
70+
71+
**Example file:** [computed-fields-example.l4](computed-fields-example.l4)
72+
4273
### Type Synonyms
4374

4475
Create an alias for an existing type:
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
-- Computed Fields Example
2+
-- Demonstrates "methods" on records: fields with MEANS clauses
3+
-- whose values are automatically derived from other fields.
4+
5+
IMPORT prelude
6+
7+
-- ─────────────────────────────────────────────────────────
8+
-- Basic computed fields (methods)
9+
-- ─────────────────────────────────────────────────────────
10+
-- Style: stored fields first, then computed/derived fields.
11+
12+
DECLARE Employee HAS
13+
-- stored fields (primary attributes)
14+
`name` IS A STRING
15+
`date of birth` IS A NUMBER
16+
`date of employment` IS A NUMBER
17+
`monthly salary` IS A NUMBER
18+
`current year` IS A NUMBER
19+
-- computed fields (derived attributes / methods)
20+
`age` IS A NUMBER
21+
MEANS `current year` - `date of birth`
22+
`years of service` IS A NUMBER
23+
MEANS `current year` - `date of employment`
24+
`senior` IS A BOOLEAN
25+
MEANS `years of service` >= 10
26+
`eligible` IS A BOOLEAN
27+
MEANS `senior`
28+
AND `monthly salary` <= 6000
29+
30+
-- Construction: only stored fields are supplied.
31+
-- Computed fields are derived automatically.
32+
veteran MEANS Employee WITH
33+
`name` IS "Lim"
34+
`date of birth` IS 1960
35+
`date of employment` IS 1990
36+
`monthly salary` IS 3000
37+
`current year` IS 2026
38+
39+
-- Access computed fields with 's, just like stored fields.
40+
#EVAL veteran's `age` -- 66
41+
#EVAL veteran's `years of service` -- 36
42+
#EVAL veteran's `senior` -- TRUE
43+
#EVAL veteran's `eligible` -- TRUE
44+
45+
-- ─────────────────────────────────────────────────────────
46+
-- Calling external functions from computed fields
47+
-- ─────────────────────────────────────────────────────────
48+
-- Multi-argument calls can use juxtaposition, OF, or mixfix:
49+
-- `apply rate` `balance` `rate` -- juxtaposition
50+
-- `apply rate` OF `balance`, `rate` -- OF (comma-separated)
51+
-- `apply` `balance` `at rate` `rate` -- mixfix (natural language)
52+
-- All are equivalent. Choose whichever reads best in context.
53+
54+
GIVEN `base` IS A NUMBER
55+
`rate` IS A NUMBER
56+
GIVETH A NUMBER
57+
`apply rate` MEANS `base` * `rate` / 100
58+
59+
DECLARE Account HAS
60+
`balance` IS A NUMBER
61+
`rate` IS A NUMBER
62+
`interest` IS A NUMBER
63+
MEANS `apply rate` `balance` `rate`
64+
`projected` IS A NUMBER
65+
MEANS `balance` + `interest`
66+
67+
savings MEANS Account WITH
68+
`balance` IS 10000
69+
`rate` IS 5
70+
71+
#EVAL savings's `interest` -- 500.0
72+
#EVAL savings's `projected` -- 10500.0
73+
74+
-- ─────────────────────────────────────────────────────────
75+
-- WHERE and LET/IN inside computed fields
76+
-- ─────────────────────────────────────────────────────────
77+
78+
GIVEN `x` IS A NUMBER
79+
GIVETH A NUMBER
80+
`square` MEANS `x` * `x`
81+
82+
DECLARE Shape HAS
83+
`width` IS A NUMBER
84+
`height` IS A NUMBER
85+
`area` IS A NUMBER
86+
MEANS `width` * `height`
87+
`diag sq` IS A NUMBER
88+
MEANS `result`
89+
WHERE
90+
`w2` MEANS `square` `width`
91+
`h2` MEANS `square` `height`
92+
`result` MEANS `w2` + `h2`
93+
`scaled` IS A NUMBER
94+
MEANS LET `factor` MEANS 2 IN `area` * `factor`
95+
96+
rect MEANS Shape WITH
97+
`width` IS 3
98+
`height` IS 4
99+
100+
#EVAL rect's `area` -- 12
101+
#EVAL rect's `diag sq` -- 25
102+
#EVAL rect's `scaled` -- 24

jl4-actus-analyzer/src/L4/ACTUS/FeatureExtractor.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ conName :: ConDecl Resolved -> Text
516516
conName (MkConDecl _ name _) = nameText name
517517

518518
fieldName :: TypedName Resolved -> Text
519-
fieldName (MkTypedName _ name _) = nameText name
519+
fieldName (MkTypedName _ name _ _) = nameText name
520520

521521
isCurrencyName :: Text -> Bool
522522
isCurrencyName name =

0 commit comments

Comments
 (0)