Syntax Cheat Sheet
A dense single-page reference covering all syntax forms, operators, common patterns, and key prelude functions.
Primitives
| Type | Syntax | Examples |
|---|---|---|
| Integer | digits | 42, -7, 0 |
| Float | digits with . | 3.14, -0.5 |
| String | double quotes | "hello", "line\nbreak" |
| Symbol | colon prefix | :key, :name |
| Boolean | literals | true, false |
| Null | literal | null |
| ZDT | t"..." prefix | t"2024-03-15", t"2024-03-15T14:30:00Z" |
Blocks
# Property declaration
name: expression
# Function declaration
f(x, y): expression
# Operator declaration (binary)
(l ++ r): expression
# Operator declaration (prefix / postfix)
(! x): expression
(x ******): expression
# Block literal
{ a: 1 b: 2 c: 3 }
# Commas are optional
{ a: 1, b: 2, c: 3 }
# Nested blocks
{ outer: { inner: "value" } }
Top-level unit: the file itself is an implicit block (no braces needed).
Lists
# List literal
[1, 2, 3]
# Empty list
[]
# Mixed types
[1, "two", :three, true]
String Interpolation
# Insert expressions with {braces}
"Hello, {name}!"
# String anaphora (defines a function)
"#{}" # one-parameter function
"{0} and {1}" # two-parameter function
Comments
# Line comment (to end of line)
x: 42 # inline comment
Declarations
| Form | Syntax | Notes |
|---|---|---|
| Property | name: expr | Defines a named value |
| Function | f(x, y): expr | Named function with parameters |
| Block pattern | f({x y}): expr | Destructures block argument fields |
| Block rename | f({x: a y: b}): expr | Destructures with renamed bindings |
| List pattern | f([a, b, c]): expr | Destructures fixed-length list |
| Cons pattern | f([h : t]): expr | Destructures head and tail of list |
| Binary operator | (l op r): expr | Infix operator |
| Prefix operator | (op x): expr | Unary prefix |
| Postfix operator | (x op): expr | Unary postfix |
| Idiot bracket | ⟦ xs ⟧: expr | Custom Unicode bracket pair |
Idiot Brackets
Idiot brackets allow custom Unicode bracket pairs that collect their content items into a list and pass it to a function.
# Declare a bracket pair function (xs receives a list)
⟦ xs ⟧: xs map(_ * 2)
# Items are collected as a list: ⟦ a b c ⟧ = ⟦⟧([a, b, c])
result: ⟦ 3 4 5 ⟧ # [6, 8, 10]
Any matching Unicode bracket pair works, e.g. ⟦⟧, ⟨⟩, ⟪⟫, ⌈⌉, ⌊⌋, ‹›, ⦃⦄, ⦇⦈, ⦉⦊, «»,
【】, 〔〕, 〖〗, 〘〙, 〚〛.
The prelude defines ⌈⌉ and ⌊⌋ for ceiling and floor:
⌈3.2⌉ # 4
⌊3.8⌋ # 3
⌈-1.5⌉ # -1
⌊-1.5⌋ # -2
Monadic Blocks
Monadic sequencing (like do-notation) is supported via bracket pairs or block
metadata. A bracket block or annotated block followed by .expr desugars to a
bind chain.
# Bracket pair definition — explicit functions
⟦{}⟧: { :monad bind: my-bind return: my-return }
# Bracket pair definition — namespace reference
⟦{}⟧: { :monad namespace: my-monad }
# Block metadata forms (all followed by .return_expr):
{ :name decls }.expr # Form 1: bare symbol namespace
{ { monad: name } decls }.expr # Form 2: monad key
{ { :monad namespace: name } decls }.expr # Form 3: tagged namespace
{ { :monad bind: f return: r } decls }.expr # Form 4: explicit inline
# ⟦ a: ma b: mb ⟧.return_expr
# desugars to: bind(ma, (a): bind(mb, (b): return(return_expr)))
result: ⟦ a: ma b: mb ⟧.(a + b)
All declarations are bind steps whose names are in scope for later declarations
and the return expression. The return expression follows the closing bracket (or
block) as .name, .(expr), or .[list].
Built-in monadic namespaces:
| Tag | Monad | Result type |
|---|---|---|
:io | IO effects | IO action |
:random | Random state | Random action |
:state | Block state (import state.eu) | State action |
:let | Identity (sequential bindings) | Value |
:for | List (comprehensions) | List |
# List comprehension: cartesian product with filter
{ :for x: [1, 2, 3], _: [x] filter(> 1) }.(x * 10)
# => [20, 30]
Metadata Annotations
# Declaration metadata (backtick prefix)
` "Documentation string"
name: value
# Structured metadata
` { doc: "description" associates: :left precedence: 50 }
(l op r): expr
# Unit-level metadata (first expression in file)
{ :doc "Unit description" }
a: 1
Special metadata keys: :target, :suppress, :main,
associates, precedence, import.
Function Application
# Parenthesised application (no whitespace before paren)
f(x, y)
# Catenation (pipeline style, single argument)
x f # equivalent to f(x)
x f g h # equivalent to h(g(f(x)))
# Partial application (curried)
add(1) # returns a function adding 1
# Sections (operator with gaps)
(+ 1) # function: add 1
(* 2) # function: multiply by 2
(/) # function: floor divide (two params)
(÷) # function: precise divide (two params)
Lookup and Generalised Lookup
# Simple lookup
block.key
# Generalised lookup (evaluate RHS in block's scope)
{ a: 3 b: 4 }.(a + b) # 7
{ a: 3 b: 4 }.[a, b] # [3, 4]
{ a: 3 b: 4 }."{a} and {b}" # "3 and 4"
Anaphora (Implicit Parameters)
| Type | Numbered | Unnumbered | Scope |
|---|---|---|---|
| Expression | _0, _1, _2 | _ (each use = new param) | Expression |
| Block | •0, •1, •2 | • (each use = new param) | Block |
| String | {0}, {1}, {2} | {} (each use = new param) | String |
# Expression anaphora
map(_0 * _0) # square each element
map(_ + 1) # increment (each _ is a new param)
# Block anaphora (bullet = Option-8 on Mac)
{ x: •0 y: •1 } # two-parameter block function
# String anaphora
map("item: {}") # format each element
Operator Precedence Table
From highest to lowest binding:
| Prec | Name | Assoc | Operators | Description |
|---|---|---|---|---|
| 95 | -- | prefix | ↑ | Tight prefix (head) |
| 90 | lookup | left | . | Field access / lookup |
| 88 | bool-unary | prefix | !, ¬ | Boolean negation |
| 88 | bool-unary | postfix | ✓ | Not-null check (true if not null) |
| 85 | exp | right | ^, ∘, ; | Power, composition |
| 80 | prod | left | *, /, ÷, % | Multiplication, floor division, precise division, floor modulo |
| 75 | sum | left | +, - | Addition, subtraction |
| 55 | -- | right | ‖ | List cons (prepend element) |
| 50 | cmp | left | <, >, <=, >= | Comparison |
| 45 | append | right | ++, << | List append, deep merge |
| 42 | map | left | <$> | Functor map |
| 40 | eq | left | =, != | Equality |
| 35 | bool-prod | left | &&, ∧ | Logical AND |
| 30 | bool-sum | left | ||, ∨ | Logical OR |
| 20 | cat | left | (catenation) | Juxtaposition / pipeline |
| 10 | apply | right | @ | Function application |
| 5 | meta | right | //, //<<, //=, //=>, //=?, //=?>, //! | Metadata / assertions |
User-defined operators default to left-associative, precedence 50.
Set custom values via metadata: ` { precedence: 75 associates: :right }
Named precedence levels for use in metadata: lookup, call,
bool-unary, exp, prod, sum, shift, bitwise, cmp,
append, map, eq, bool-prod, bool-sum, cat, apply, meta.
Block Merge
# Catenation of blocks performs a shallow merge
{ a: 1 } { b: 2 } # { a: 1 b: 2 }
{ a: 1 } { a: 2 } # { a: 2 }
# Deep merge operator
{ a: { x: 1 } } << { a: { y: 2 } } # { a: { x: 1 y: 2 } }
Imports
# Unit-level import
{ import: "lib.eu" }
# Named import
{ import: "cfg=config.eu" }
# Multiple imports
{ import: ["dep-a.eu", "dep-b.eu"] }
# Format override
{ import: "yaml@data.txt" }
# Git import
{ import: { git: "https://..." commit: "sha..." import: "file.eu" } }
Key Prelude Functions
Lists
| Function | Description |
|---|---|
head | First element |
tail | All but first |
cons(x, xs) | Prepend element |
map(f) | Transform each element |
filter(p?) | Keep elements matching predicate |
foldl(f, init) | Left fold |
foldr(f, init) | Right fold |
sort-by(f) | Sort by key function |
take(n) | First n elements |
drop(n) | Remove first n |
zip | Pair elements from two lists |
zip-with(f) | Combine elements with function |
flatten | Flatten nested lists one level |
reverse | Reverse a list |
count | Number of elements |
range(a, b) | Integers from a to b-1 |
nil? | Is the list empty? |
any?(p?) | Does any element match? |
all?(p?) | Do all elements match? |
unique | Remove duplicates |
Blocks
| Function | Description |
|---|---|
lookup(key) | Look up a key (symbol) |
lookup-or(key, default) | Look up with default |
has(key) | Does block contain key? |
keys | List of keys (as symbols) |
values | List of values |
elements | List of {key, value} pairs |
map-keys(f) | Transform keys |
map-values(f) | Transform values |
select(keys) | Keep only listed keys |
dissoc(keys) | Remove listed keys |
merge(b) | Shallow merge |
deep-merge(b) | Deep recursive merge |
sort-keys | Sort by key name |
Strings (str namespace)
| Function | Description |
|---|---|
str.len(s) | String length |
str.upper(s) | Upper case |
str.lower(s) | Lower case |
str.starts-with?(prefix) | Starts with prefix? |
str.ends-with?(suffix) | Ends with suffix? |
str.contains?(sub) | Contains substring? |
str.matches?(regex) | Matches regex? |
str.split(sep) | Split by separator |
str.join(sep) | Join list with separator |
str.replace(from, to) | Replace occurrences |
str.trim | Remove surrounding whitespace |
Serialisation and Parsing
| Function | Description |
|---|---|
render(value) | Serialise to YAML string |
render-as(fmt, value) | Serialise to string in named format |
parse-as(fmt, str) | Parse string as structured data (inverse of render-as) |
Formats for render-as: :yaml, :json, :toml, :text, :edn, :html.
Formats for parse-as: :json, :yaml, :toml, :csv, :xml, :edn, :jsonl.
parse-as is safe for untrusted input — YAML !eu tags are never evaluated.
Combinators
| Function | Description |
|---|---|
identity | Returns its argument unchanged |
const(k) | Always returns k |
compose(f, g) or f ∘ g | Compose functions |
flip(f) | Swap argument order |
complement(p?) | Negate a predicate |
curry(f) | Curry a function taking a pair |
uncurry(f) | Uncurry to take a pair |
Numbers
| Function | Description |
|---|---|
num | Parse string to number |
abs | Absolute value |
negate | Negate number |
inc / dec | Increment / decrement |
max(a, b) / min(a, b) | Maximum / minimum |
even? / odd? | Parity predicates |
zero? / pos? / neg? | Sign predicates |
floor / ceiling / ⌊n⌋ / ⌈n⌉ | Rounding (no round) |
Arrays (arr namespace)
| Function | Description |
|---|---|
arr.zeros(shape) | Create array of zeros; shape is a list of integers |
arr.fill(shape, val) | Create array filled with val |
arr.from-flat(shape, vals) | Create array from flat list of numbers |
a arr.get(coords) | Element at coordinate list coords |
a arr.set(coords, val) | New array with element at coords set to val |
arr.shape(a) | Shape as list of integers |
arr.rank(a) | Number of dimensions |
arr.length(a) | Total number of elements |
arr.to-list(a) | Flat list of elements in row-major order |
arr.array?(x) / is-array?(x) | Is x an array? |
arr.transpose(a) | Reverse all axes |
a arr.reshape(shape) | Reshape (total elements must match) |
a arr.slice(axis, idx) | Slice along axis at idx (reduces rank by 1) |
arr.add(a, b) / arr.sub / arr.mul / arr.div | Element-wise arithmetic; b may be scalar |
a !! coords | Index operator; for arrays, coords is a list e.g. [row, col] |
arr.indices(a) | List of coordinate lists for every element (row-major) |
arr.map(f, a) | Apply f to each element; same shape |
arr.map-indexed(f, a) | Apply f(coords, val) to each element; same shape |
arr.fold(f, init, a) | Left-fold over all elements in row-major order |
a arr.neighbours(coords, offsets) | Values at valid in-bounds neighbours given offset vectors |
The standard +, -, *, / operators are polymorphic and apply element-wise when
either operand is an array. Scalar broadcasting is supported.
IO
| Binding / Function | Description |
|---|---|
io.env | Block of environment variables |
io.epoch-time | Unix timestamp at launch |
io.args | Command-line arguments (after --) |
io.random | Infinite lazy stream of random floats |
io.RANDOM_SEED | Current random seed |
io.return(a) | Wrap a pure value in the IO monad |
io.bind(action, cont) | Sequence two IO actions |
io.shell(cmd) | Run cmd via sh -c; returns {stdout, stderr, exit-code} |
io.shell-with(opts, cmd) | Run cmd with extra options (e.g. {stdin: s, timeout: 60}). Pipeline: "cmd" shell-with(opts) |
io.exec([cmd : args]) | Run cmd directly (no shell); argument is a list [cmd, arg1, arg2, ...] |
io.exec-with(opts, [cmd : args]) | Run cmd directly with extra options. Pipeline: ["git", "log"] exec-with(opts) |
io.check(result) | Fail with stderr if exit-code is non-zero; otherwise return result |
io.map(f, action) | Apply pure function to result of IO action (fmap) |
Test / Assertion Operators
Test expectations (panic in normal mode, return false in test mode):
| Operator | Description |
|---|---|
e //= v | Expect e equals v, return true/false |
e //=? f | Expect f(e) is true, return true/false |
e //! | Expect e is true, return true/false |
Assertions (return e, panic on failure in normal mode):
| Operator | Description |
|---|---|
e //=> v | Assert e equals v, return e |
e //=?> f | Assert f(e) is true, return e |
Command Line Quick Reference
eu file.eu # Evaluate file, output YAML
eu -j file.eu # Output JSON
eu -x text file.eu # Output plain text
eu -e 'expression' # Evaluate expression
eu a.yaml b.eu # Merge inputs
eu -t target file.eu # Render specific target
eu list-targets file.eu # List targets
eu --seed 42 file.eu # Deterministic random
eu -Q file.eu # Suppress prelude
eu fmt file.eu # Format source
eu dump stg file.eu # Dump STG syntax
eu -- arg1 arg2 # Pass arguments (io.args)
eu check file.eu # Type-check, report warnings
eu check --strict file.eu # Treat type warnings as errors
eu --type-check file.eu # Check then evaluate
Type Checking
Type annotations live in declaration metadata alongside doc strings.
All annotations are optional — unannotated code is treated as any.
Annotation syntax
` { doc: "`double(x)` - double a number."
type: "number -> number" }
double(x): x * 2
# Operator
` { doc: "..."
type: "a -> (a -> b) -> b"
associates: :left
precedence: 20 }
(x |> f): f(x)
Type aliases
# In unit metadata
{ types: { Person: "{name: string, age: number, ..}" } }
# Via type-def: on a canonical instance
` { type-def: "Point" }
origin: { x: 0, y: 0 }
Type forms
| Syntax | Meaning |
|---|---|
number | number |
string | string |
symbol | symbol |
bool | boolean |
null | null |
datetime | zoned date-time |
any | gradual/unknown — no type errors |
[T] | homogeneous list of T |
(A, B) | 2-tuple; (A, B, C) for triple |
(A,) | 1-tuple |
{k: T} | closed record |
{k: T, ..} | open record (at least k: T) |
block | any block (no known shape) |
A -> B | function |
A | B | union |
a, b, s | type variable (lowercase) |
IO(T) | IO action producing T |
Lens(a, b) | lens focusing on b within a |
Traversal(a, b) | traversal over b's within a |
set | ordered set of primitives |
vec | flat vector of primitives |
array | n-dimensional number array |