Syntax Reference

Everything you need to read and write Loon.

Delimiters

Loon has a small set of delimiters, and each one means exactly one thing. Square brackets are the workhorse of the language: every function call, every special form, every operation lives inside them. The other bracket types are reserved for data literals.

[...]

Function call or special form. The first element is always the operator.

{...}

Map literal. Alternating key-value pairs: {:name "Ada" :age 30}

#[...]

Vector literal: #[1 2 3]

#{...}

Set literal: #{"a" "b" "c"}

(...)

Reserved. Not used in Loon syntax.

Literals

Loon's literal types are straightforward. Strings are double-quoted, numbers are either ints or floats, and keywords start with a colon. One thing worth noting: there is no nil or null in Loon. If you need to represent the absence of a value, you use None from the Option type.

"hello"

String. Double-quoted, supports escape sequences (\n, \t, \", \\).

42

Integer. 64-bit signed.

3.14

Float. 64-bit double precision.

true / false

Boolean values.

:name

Keyword. A lightweight identifier, often used as map keys.

None

Absence of a value. Part of the Option ADT.

Empty

Empty collection sentinel.

Special Forms

Special forms look like function calls but have their own evaluation rules. For example, if does not evaluate both branches, and let introduces a new binding rather than calling anything. These are the building blocks you will use constantly.

fn

Define a named or anonymous function: [fn name [params] body] or [fn [params] body]

let

Bind a value to a name: [let x 42]

if

Conditional: [if cond then else]. Branches do NOT have implicit do.

do

Evaluate multiple expressions, return the last: [do expr1 expr2]

match

Pattern matching with positional pairs: [match val pat1 body1 pat2 body2]. Expression guards supported.

type

Define an algebraic data type.

effect

Declare an effect with operations.

handle

Handle effects with an effect handler.

macro

Define a macro that operates on syntax.

macro+

Define a type-aware macro (expanded after type checking).

test

Define a test: [test "name" [params] body]

use

Import a module or members from a module.

pub

Export a binding from a module.

sig

Type signature annotation.

try

Attempt an operation that may fail.

pipe

Thread a value through a series of transformations: [pipe x f g h]

quote

Prevent evaluation of an expression.

unquote

Evaluate inside a quoted expression.

derive

Derive trait implementations for a type.

Comments

Line comments begin with ; and extend to the end of the line. You can put them on their own line or tack them onto the end of an expression.

; this is a comment
[let x 42]  ; inline comment

String Interpolation

All strings support inline interpolation with {expr}. Any expression inside braces is evaluated and coerced to a string. Escape literal braces with a backslash.

[let name "world"]
"hello, {name}!"          ; "hello, world!"
"2 + 2 = {[+ 2 2]}"      ; "2 + 2 = 4"

Quasiquoting

Quasiquoting is mainly used inside macros. A quasiquoted expression is like a template: most of it is taken literally, but you can selectively "unquote" parts to splice in computed values.

`expr

Quasiquote. Like quote but allows unquoting inside.

~expr

Unquote. Evaluate expr inside a quasiquote.

~@expr

Splice-unquote. Evaluate and splice a list into the surrounding form.

Operators

In Loon, operators are just regular functions. There is no special operator precedence to memorize because everything is called with bracket syntax. [+ 1 2] is a function call to + with two arguments, exactly like [map f xs] is a function call to map.

+ - * / %

Arithmetic. Work on integers and floats.

= !=

Equality and inequality. Work on all types.

< > <= >=

Ordering comparisons. Work on numbers and strings.

not

Logical negation.

and / or

Logical conjunction and disjunction. These are eager, not short-circuiting.

and/or evaluate both arguments eagerly. Use nested if for short-circuit logic.