Special forms
A special form is recognised by the analyser at the head of a list. Special forms aren’t values: they can’t be passed around, and a local of the same name doesn’t shadow them.
Shapes are given in block syntax, on one line; each is equally a list — if: p a b is (if p a b).
The examples under each show how they’re laid out in practice.
Top level
Section titled “Top level”These are only allowed at the top level of a namespace, or inside a top-level do:.
def: name value- Defines
nameas the value ofvalue. def: name(p1, p2, …) body…- Defines a function; equivalent to
def: name fn: name(p1, p2, …) body…. A definition can’t see itself: a function can’t call itself by name — userecur. Metadata on thedef:—^.test def: …— is recorded on the var.def: answer 42def: area(w, h)mul(w, h) defmacro: name(p1, p2, & rest) body…- Defines a macro. See macros.
decl: …- Declares a key or a type. See types and keys.
tag: …,enum: …- Declare a tag or an enum. See tags and enums.
defx: …- Declares an effect. See effects.
ns: …- Names the namespace; only as the first form of a file. See namespaces.
Sequencing and binding
Section titled “Sequencing and binding”do: form…- Evaluates each form in order, returning the value of the last.
Requires at least one form.
do:println("working")42
let: [name1 value1, name2 value2, …] body…- Binds each name to its value, in order — each value can refer to the names before it — then evaluates the body.
Names are symbols; there is no destructuring.
let: [w 3h 4]mul(w, h)
Functions
Section titled “Functions”fn: name(p1, p2, …) body…- A function value.
The name is required — it appears in stack traces — but isn’t bound in the body.
Parameters are symbols; a function has one fixed arity.
Closures capture the locals they refer to.
mapv(xs, fn: double(x) mul(x, 2))def: adder(n)fn: addN(x)add(x, n)
Conditionals
Section titled “Conditionals”if: test then else- Evaluates
test, which must be aBool, thenthenorelse. Exactly three forms; for a one-armedif, seewhen.if: gt(n, 0)"positive""not positive" ifLet: [name value] then else- Evaluates
value; if it isn’tnil, binds it tonameand evaluatesthen, otherwise evaluateselse.nameis in scope inthenonly, where its type is narrowed past nil.ifLet: [nick .?nickname(user)]nick.name(user) case: value pattern1 body1 pattern2 body2 … default?- Evaluates
valueand the body of the first pattern that matches it. A final, unpaired form is the default.case: shapeCircle{radius}mul(3, mul(radius, radius))Square{side}mul(side, side)0
A key in aPattern Matches Tag,m/Taga value with that tag, whatever its record Tag(r)a value with that tag, binding it to rTag{k1, k2}a value with that tag, binding k1to its.k1andk2to its.k2x— lowercaseany value, nil included, binding it to xTag{…}pattern is looked up among the tag’s own keys first, then in scope, and must be one the tag declares.nilis not a pattern — branch on it withifLet. Without a default or a catch-all, acaseover an enum’s tags must name every variant:Non-exhaustive case: missing variants … of enum …. Tags from different enums, or standalone tags, can’t be cased together:case patterns A and B belong to different enums.
loop: [name1 init1, name2 init2, …] body…- Binds the names, like
let:, and evaluates the body. recur(v1, v2, …)- Re-binds the innermost
loop:’s names — or the enclosing function’s parameters — and starts its body again. Takes one value per binding, and is only allowed in tail position:recuranywhere else is an error.loop: [i 1total 0]if: gt(i, 10)totalrecur(add(i, 1), add(total, i))
Records
Section titled “Records”with(record, .k1 v1, .k2 v2, …)- A copy of
record, with each key set to its value — added if absent.with(state, .currentTerm newTerm, .votedFor nil)
Errors
Section titled “Errors”try: body… catch: pattern1 handler1 … finally: cleanup…- Evaluates the body.
If it throws, the thrown anomaly is matched against the
catch:clauses’ patterns, as incase:; a final unpaired form is the default. Atry:needs at least onecatch:clause, and may have several, whose branches are combined in order.finally:runs whether or not the body threw.See errors.try:findUser(id)catch: NotFound(e)nilcatch: Host(e)throw(Fault{.exnMessage "lookup failed"})finally:closeConnection()
Effects
Section titled “Effects”withFx: [effect1 value1, effect2 value2, …] body…- Binds each effect to its value for the evaluation of the body.
See effects.withFx: [now fixedClock, log quietly]completeTodo(milk)
Host languages
Section titled “Host languages”lang(language, Type, code)- Evaluates
codein another GraalVM language, taking its result to be of typeType.languageandcodeare string literals; the language must be installed alongside Bridje.
Quoting
Section titled “Quoting”quote(form),squote(form)- Written
'formand`form. See macros and quoting.