Towards L4 Mastery
Functional programming for legal professionals
7.1 The Functional Mindset: "Think More in a Haskell Way"
Critical insight: L4 is fundamentally a functional programming language. Understanding this is essential for writing correct L4 code.
What does "functional" mean for lawyers?
Functions over procedures: Instead of step-by-step instructions, you define what something IS
Immutable data: Legal facts don't change—you create new states rather than modifying existing ones
Composition: Complex legal rules are built by combining simpler ones
Type safety: The computer prevents logical errors by checking that everything "fits together"
Example transformation from procedural to functional thinking:
❌ Procedural mindset:
-- "Step 1: Check if application complete"
-- "Step 2: Check if purposes charitable"
-- "Step 3: If both true, register charity"✅ Functional mindset:
-- Define what completeness IS
GIVEN applicant IS A RegisterEntry
GIVETH A BOOLEAN
`application is complete` MEANS
`constitution is written` applicant
AND `has at least one purpose` applicant
AND `has valid public benefit statement` applicant
-- Define what charitable purposes ARE
GIVEN charity IS A RegisterEntry
GIVETH A BOOLEAN
`all purposes are charitable` MEANS
all (GIVEN p YIELD `is charitable purpose` p) (charity's purposes)
-- Define registration criteria using the above
GIVEN applicant IS A RegisterEntry
GIVETH A BOOLEAN
`should register` MEANS
`application is complete` applicant
AND `all purposes are charitable` applicantWhy this matters: Legal reasoning is naturally functional—we define criteria and apply them, rather than executing procedures.
7.2 Function Application Precedence - The Critical Pattern
The most important syntax rule in L4: Function application has higher precedence than field access.
This causes systematic errors that look like this:
unexpected 's
expecting &&, (, *, +, -, /, ;, <, <=, =, >, >=, AND, OR...Problem examples and solutions:
❌ Wrong - Parser confusion:
length applicant's purposes > 0 -- Parser sees: (length applicant)'s purposes > 0
all (GIVEN p YIELD...) charity's purposes -- Parser sees: (all (GIVEN p YIELD...) charity)'s purposes
elem governor charity's governors -- Parser sees: (elem governor charity)'s governors✅ Correct - Use parentheses:
length (applicant's purposes) > 0
all (GIVEN p YIELD...) (charity's purposes)
elem governor (charity's governors)The pattern: When passing field access as function arguments, always wrap in parentheses.
Why this happens:
L4 follows Haskell-like precedence:
function application > field access > comparison > boolean operatorslength applicant's purposesis parsed as(length applicant)'s purposesThe parser expects
lengthto be applied toapplicant, then tries to accesspurposesfield of the resultBut
applicantis not a list, solength applicantis a type error, and the parser gets confused
7.3 Field Access Patterns - Possessive vs Functional
L4 supports both possessive syntax and function application for field access.
When possessive syntax works:
applicant's purposes -- ✅ Fine in simple contexts
charity's constitution's isWritten -- ✅ Chained access works
governor's convictions -- ✅ Basic field accessWhen you MUST use function application:
-- In EXISTS expressions (possessive not supported)
❌ EXISTS c IN list SUCH THAT c's isSpent EQUALS FALSE
✅ EXISTS c IN list SUCH THAT isSpent c EQUALS FALSE
-- In nested function calls (precedence issues)
❌ length applicant's purposes
✅ length (applicant's purposes)
-- In quantifier arguments
❌ all (GIVEN p YIELD...) charity's purposes
✅ all (GIVEN p YIELD...) (charity's purposes)Convert possessive to functional:
object's field→field objectobject's field1's field2→field2 (field1 object)
For deeply nested access:
-- Possessive (can be problematic in complex expressions)
(lastFinancialRecord charity)'s financialYear's endDate
-- Functional (always works)
endDate (financialYear (lastFinancialRecord charity))7.4 List Operations and Quantifiers
Key insight: Use prelude functions instead of inventing syntax.
❌ Non-existent syntax:
EXISTS c IN list SUCH THAT condition -- Not valid L4
EVERY x IS A Type WHERE condition -- Not valid L4
FOR ALL x IN list CHECK condition -- Not valid L4✅ Use prelude functions:
-- For "exists" - use `any`
any (GIVEN c YIELD condition) list
-- For "all" - use `all`
all (GIVEN p YIELD condition) list
-- For "none" - use NOT and any
NOT any (GIVEN x YIELD condition) list
-- For filtering - use `filter`
filter (GIVEN x YIELD condition) listPattern for quantified conditions:
-- Template: quantifier (GIVEN variable YIELD condition) list
-- Example: "All governors have no unspent convictions"
all (GIVEN gov YIELD NOT any (GIVEN c YIELD isSpent c EQUALS FALSE) (gov's convictions)) governors
-- Example: "Some purpose is charitable"
any (GIVEN p YIELD `is charitable purpose` p) purposes
-- Example: "No governor is disqualified"
NOT any (GIVEN gov YIELD gov's isDisqualified EQUALS TRUE) governors7.5 Type Safety and Missing Functions
Problem: L4's prelude is minimal—common functions may be missing.
Systematic approach:
Check if function exists in prelude (like
length,elem,filter)Define missing functions at the top of your file
Use proper types (don't mix STRING and LIST operations)
Essential missing functions to define:
-- String length (often needed, not in prelude)
GIVEN str IS A STRING
GIVETH A NUMBER
stringLength str MEANS
-- Implementation depends on L4 version
0 -- Placeholder, or use string comparison patterns
-- List length (also missing from prelude)
GIVEN a IS A TYPE
list IS A LIST OF a
GIVETH A NUMBER
length list MEANS
CONSIDER list
WHEN EMPTY THEN 0
WHEN x FOLLOWED BY xs THEN 1 + length xs
-- Safe list access
GIVEN a IS A TYPE
list IS A LIST OF a
index IS A NUMBER
GIVETH A MAYBE a
safeAt list index MEANS
IF index < 0 OR index >= length list
THEN NOTHING
ELSE JUST (at list index)Type checking patterns:
-- ❌ Type error: mixing string and list operations
length someString > 0 -- length expects LIST, gets STRING
-- ✅ Correct: check string non-emptiness
NOT someString EQUALS "" -- STRING comparison
-- ✅ Correct: check list non-emptiness
length someList > 0 -- LIST operation7.6 Boolean Logic and Comparison Patterns
Prefer direct comparisons over negation:
❌ Unnecessarily complex:
NOT c's isSpent EQUALS TRUE
NOT governor's isDisqualified EQUALS TRUE✅ Cleaner and clearer:
c's isSpent EQUALS FALSE
governor's isDisqualified EQUALS FALSEComparison precedence patterns:
-- ✅ Simple comparisons work naturally
age >= 18
amount > 1000
date EQUALS today
-- ✅ Parentheses for complex expressions
(length purposes) > 0
(governor's age) >= minimumAge
(charity's income's amount) > thresholdBoolean combination patterns:
-- AND/OR precedence (AND binds tighter than OR)
condition1 AND condition2 OR condition3 -- Means: (condition1 AND condition2) OR condition3
condition1 OR condition2 AND condition3 -- Means: condition1 OR (condition2 AND condition3)
-- Use parentheses for clarity
(condition1 OR condition2) AND condition3
condition1 AND (condition2 OR condition3)7.7 Systematic Debugging Approach
When you see syntax errors, follow this checklist:
Function precedence: Are field accesses in function arguments wrapped in parentheses?
Missing functions: Is the function defined in prelude or your file?
Type mismatches: Are you using STRING operations on LISTs or vice versa?
Possessive syntax: Can you convert to function application form?
Boolean logic: Are comparisons and boolean operations correctly parenthesized?
Error patterns and fixes:
Error: "unexpected 's"
→ Fix: Add parentheses around field access in function arguments
Error: "could not find definition for LENGTH"
→ Fix: Define length function at top of file
Error: "expecting BOOLEAN, got STRING"
→ Fix: Use correct comparison (EQUALS "", not length > 0)
Error: "could not find definition for EXISTS"
→ Fix: Use any (GIVEN x YIELD condition) listSuccess Check: You now understand L4's functional nature, can handle precedence correctly, use proper quantifiers, and systematically debug type and syntax errors.
Last updated
