Fix errors
Read an L4 error message, find the cause, and fix it. The error message reference lists the errors one by one; this page shows how to work through them.
Read a diagnostic
Run l4 check myfile.l4, or open the file in VS Code, which shows the same messages as red underlines. Each problem is printed as a block:
File: err-layout.l4
Range: 4:1-4:1
Source: parser
Severity: DiagnosticSeverity_Error
Message:
|
4 | x TIMES 2
| ^
incorrect indentation (got 1, should be greater than 1)
Read three things, in this order:
- Range:
line:column-line:columnof the code at fault. Go there first. - Source:
parsermeans the file could not be read as L4 at all, usually because of layout or a misplaced keyword.checkmeans it was read but the types do not fit together. - Message: the explanation, in plain words. Read all of it before guessing.
When there are several errors, fix the first one and check again: later errors are often caused by the first. l4 check exits with 0 when the file is valid and 1 otherwise.
A debugging routine
- Run
l4 check file.l4after every few edits. It is fast because it evaluates nothing. - Read the first diagnostic only. Note its source (
parserorcheck) and go to its range. - For a
parsererror, look at the line above as well: a layout problem is often caused by the construct before it. - For a
checkerror, find the pair "expected X … but is here of type Y" and decide which side is wrong. - Still can't see it? Comment out half of the file (
--per line, or{- ... -}around a block), check again, and narrow it down. - Search the error message reference for a phrase from the message.
Common errors
Indentation
L4 is layout-sensitive: the body of a definition must be indented further than the line that introduces it. The diagnostic above comes from this file:
GIVEN x IS A NUMBER
GIVETH A NUMBER
DECIDE `twice` IS
x TIMES 2
Indent the body ( x TIMES 2) or keep it on the same line. Any parser error that says "incorrect indentation" or "unexpected …" is usually a layout problem. See Incorrect indentation.
Type mismatch
The GIVETH type is a promise that the body has to keep:
GIVEN x IS A NUMBER
GIVETH A BOOLEAN
DECIDE `is large` IS x PLUS 1
The type of this definition must match its type signature at err-mismatch.l4:2:8-17, namely
BOOLEAN
but is here of type
NUMBER
Decide which side is wrong. Here it is the body: DECIDE `is large` IF x GREATER THAN 1000. L4 never converts types for you; when you need a conversion, write it (see Coercions). See Types.
Unknown name
A file defines `is an adult` for a Person, and a test misspells it as #EVAL `is an adutl` `Alice`:
I could not find a definition for the identifier
`is an adutl`
which I have inferred to be of type:
FUNCTION FROM Person TO res11
The second half shows how the unknown name is used: here, as a function from Person to something not yet known (res11). Use that to find the definition you meant. In order of likelihood:
- Spelling, including spaces inside backticks:
`is an adult`and`is an adult `are different names. - Missing backticks:
is an adultwithout backticks is three separate words. - The definition does not exist: order does not matter for top-level definitions, but the name must be defined in the file or in an imported module.
Missing IMPORT
A library name such as Date only exists after you import the library that defines it. Without IMPORT daydate, the line `the closing date` MEANS Date 15 1 2025 gives "I could not find a definition for the identifier Date", the same message as a typo. Add the import:
IMPORT daydate
`the closing date` MEANS Date 15 1 2025
#EVAL `the closing date`
If an unknown name is one you did not define yourself, check the library reference for the library that provides it. See I could not find a definition for the identifier and Module not found.
Ambiguous operator: mixed types in a comparison
Comparison operators such as AT LEAST work on numbers, strings and booleans, as long as both sides have the same type. Compare a number with a string and L4 cannot choose:
DECLARE Person
HAS `name` IS A STRING
`age` IS A NUMBER
GIVEN person IS A Person
GIVETH A BOOLEAN
DECIDE `is an adult` IF
person's `age` AT LEAST "18"
There are multiple definitions for the identifier
`__GEQ__`
and I do not have sufficient information to make a choice between them.
The options are:
`__GEQ__` (predefined) of type FUNCTION FROM NUMBER AND NUMBER TO BOOLEAN
`__GEQ__` (predefined) of type FUNCTION FROM STRING AND STRING TO BOOLEAN
`__GEQ__` (predefined) of type FUNCTION FROM BOOLEAN AND BOOLEAN TO BOOLEAN
__GEQ__ is the internal name of AT LEAST (>=). None of the listed versions takes a NUMBER and a STRING, which is the problem. Make both sides the same type: person's `age` AT LEAST 18.
The same "multiple definitions" message appears with __PLUS__ and other operators when an imported library adds versions of them (daydate adds PLUS for dates) and the operands do not show which one you mean. Fix a mistyped literal, or give one operand a known type.
@export with a function-typed input
A rule marked @export cannot take a function as an input, because a function cannot be sent as JSON. The error is "Function type inputs are not supported for @export". See ASSUME and @export.
Next steps
- Error message reference: every error message, with fixes.
- Test your rules: once the file type-checks, check that it gives the right answers.