Lea Syntax Guide
A comprehensive guide to Lea's syntax.
Comments
-- Single line comment
Bindings
let x = 10 -- Immutable binding
maybe counter = 0 -- Mutable binding
counter = 5 -- Reassign mutable binding
Primitive Types
42 -- Number (Int)
3.14 -- Number (Float)
"hello" -- String
true -- Boolean
false -- Boolean
Strings
Concatenation
The ++ operator concatenates strings with automatic type coercion:
"Hello" ++ " World" -- "Hello World"
"The answer is " ++ 42 -- "The answer is 42"
"Done: " ++ true -- "Done: true"
Template Strings
Use backticks with {expr} for interpolation:
let name = "World"
`Hello {name}!` -- "Hello World!"
`Sum: {10 + 20}` -- "Sum: 30"
`Items: {[1, 2, 3]}` -- "Items: [1, 2, 3]"
Lists
[1, 2, 3]
["a", "b", "c"]
[[1, 2], [3, 4]] -- Nested lists
-- Multi-line (trailing commas allowed)
let items = [
1,
2,
3,
]
Tuples
Fixed-size immutable collections:
let point = (10, 20)
let pair = (1, "hello") -- Mixed types allowed
fst(point) -- 10
snd(point) -- 20
Records
let user = { name: "Max", age: 99 }
user.name -- "Max"
-- Multi-line (trailing commas allowed)
let config = {
host: "localhost",
port: 8080,
}
Destructuring
Record Destructuring
let user = { name: "Alice", age: 99 }
let { name, age } = user -- Extracts name and age
Tuple/List Destructuring
let point = (10, 20)
let (x, y) = point -- x=10, y=20
let (first, second) = [1, 2, 3] -- Works with lists too
Spread Operator
In Lists
let a = [1, 2, 3]
let b = [4, 5, 6]
let combined = [...a, ...b] -- [1, 2, 3, 4, 5, 6]
In Records
let base = { x: 1, y: 2 }
let extended = { ...base, z: 3 } -- { x: 1, y: 2, z: 3 }
let updated = { ...base, y: 20 } -- { x: 1, y: 20 }
Functions
Basic Syntax
let double = (x) -> x * 2
let add = (a, b) -> a + b
Multi-Statement Bodies
let process = (x) ->
let y = x * 2
let z = y + 1
z
Default Parameters
let greet = (name, greeting = "Hello") -> greeting ++ " " ++ name
greet("World") -- "Hello World"
greet("World", "Hi") -- "Hi World"
Ignored Parameters
let ignoreSecond = (x, _) -> x
Type Annotations
-- Single parameter
let double = (x) -> x * 2 :: Int :> Int
-- Multiple parameters
let add = (a, b) -> a + b :: (Int, Int) :> Int
-- Multi-line functions
let greet = (name) -> :: String :> String
"Hello " ++ name
-- Optional types
let maybe = (x) -> x :: ?Int :> ?Int
-- List types
let sumList = (nums) -> reduce(nums, 0, (acc, x) -> acc + x) :: [Int] :> Int
Runtime Type Validation
Use #validate decorator for explicit validation:
let safe = (x) -> x * 2 :: Int :> Int #validate
Strict Mode
Enable automatic type validation for all typed functions with #strict pragma or CLI flag:
#strict
-- All typed functions are now validated at runtime
let add = (a, b) -> a + b :: (Int, Int) :> Int
add(5, 10) -- OK
add("a", "b") -- Error: Argument 'a' expected Int, got string
Or use the CLI flag:
npm run lea file.lea --strict
npm run repl -- --strict
Function Overloading
Use and to add overloads:
let add = (a, b) -> a + b :: (Int, Int) :> Int
and add = (a, b) -> a ++ b :: (String, String) :> String
add(1, 2) -- 3 (Int version)
add("a", "b") -- "ab" (String version)
Early Return
Use the return keyword for early return from functions:
let clamp = (x) ->
let doubled = x * 2
doubled > 100 ? return 100 : 0
doubled + 1
Operators
Arithmetic
a + b -- Addition
a - b -- Subtraction
a * b -- Multiplication
a / b -- Division
a % b -- Modulo
Comparison
a == b -- Equal
a != b -- Not equal
a < b -- Less than
a > b -- Greater than
a <= b -- Less than or equal
a >= b -- Greater than or equal
Ternary
condition ? trueValue : falseValue
-- Multi-line
x > 100
? "big"
: "small"
Pattern Matching
let describe = (x) -> match x
| 0 -> "zero" -- Match literal
| 1 -> "one"
| if input < 0 -> "negative" -- Guard with 'input' as matched value
| if input > 100 -> "big"
| "default" -- Default case
-- Using 'input' in result body
let double = (x) -> match x
| if input > 0 -> input * 2
| 0
Pipes
Basic Pipe />
16 /> sqrt -- sqrt(16) = 4
5 /> add(3) -- add(5, 3) - value becomes first arg
5 /> add(3, input) -- add(3, 5) - placeholder controls position
Pipe Chains
[1, 2, 3, 4, 5]
/> filter((x) -> x > 2)
/> map((x) -> x * x)
/> reduce(0, (acc, x) -> acc + x)
Spread Pipe />>>
Maps over each element:
[1, 2, 3] />>>double -- [2, 4, 6]
[1, 2, 3] />>>(x, i) -> x + i -- [1, 3, 5] (with index)
["a", "b"] />>>(x, i) -> `{i}: {x}` -- ["0: a", "1: b"]
Parallel Pipe \>
See Concurrency for details.
value
\> (x) -> x + 1
\> (x) -> x * 2
/> (a, b) -> a + b
Reverse Pipe </
For reversible functions:
let double = (x) -> x * 2
and double = (x) <- x / 2
5 /> double -- 10 (forward)
10 </ double -- 5 (reverse)
Pipelines (First-Class)
See Pipelines for details.
let processNumbers = /> double /> addOne
5 /> processNumbers
Decorators
Applied after function body:
let logged = (x) -> x * 2 #log #memo #time
let retryable = (x) -> riskyOp(x) #retry(3)
See Built-in Functions for all available decorators.
Context System
Dependency injection:
-- Define with default
context Logger = { log: (msg) -> print("[DEFAULT] " ++ msg) }
-- Override in scope
provide Logger { log: (msg) -> print("[PROD] " ++ msg) }
-- Attach to function
let greet = (name) ->
Logger.log("Hello " ++ name)
Codeblocks
Collapsible regions (for IDE support):
{-- Section name --}
let x = 10
let y = 20
{/--}
Async/Await
See Concurrency for details.
let fetchData = () -> delay(100) #async
await fetchData() /> print