Special Forms¶
Lonala has exactly 5 special forms. These are the only constructs with special evaluation rules. Everything else is either an intrinsic function or derived from these forms.
def¶
Creates or updates a root var binding.
Semantics: - Creates var in current namespace if it doesn't exist - Updates root binding if var exists (upsert) - Returns the var
Def returns the var:
Updating existing binding:
With metadata:
def errors:
(def 42 :value) ; => ERROR :syntax-error
(def :kw :value) ; => ERROR :syntax-error
(def) ; => ERROR :syntax-error
fn*¶
Creates a function with a single parameter list.
Semantics:
- Single arity only (multi-arity via match in derived fn)
- No pattern matching in parameters (use match in body)
- Body has implicit do (multiple expressions allowed)
- Variadic: [a b & rest]
Basic functions:
Variadic functions:
((fn* [& args] (first args)) 1 2 3) ; => 1
((fn* [x & rest] rest) 1 2 3) ; => [2 3]
((fn* [x & rest] x) 1 2 3) ; => 1
fn? predicate:
Implicit do in body:
Named function for recursion:
Closures capture lexical scope:
fn* is single-arity only. Use match in body for multi-arity:
;; fn* takes a single parameter vector
;; Multi-arity uses match inside the body, not multiple arities
(def multi (fn* [& args]
(match (count args)
1 :one
2 :two
_ :many)))
(multi 1) ; => :one
(multi 1 2) ; => :two
(multi 1 2 3) ; => :many
No pattern matching in parameter list. Patterns belong in match:
;; This is correct - parameters are simple bindings
((fn* [[a b]] (+ a b)) [1 2]) ; => ERROR :syntax-error
;; Use match for destructuring
((fn* [pair]
(match pair
[a b] (+ a b))) [1 2]) ; => 3
match¶
Pattern matching expression with optional guards.
Semantics:
- Patterns tried in order
- First match executes corresponding body
- when introduces a guard (boolean expression)
- No match exits process with reason [:error :badmatch %{:value v}]
- Each body is a single expression (use do for multiple)
Pattern Syntax¶
| Pattern | Matches | Binds |
|---|---|---|
x |
Anything | Yes |
_ |
Anything | No (wildcard) |
42 |
Literal 42 | No |
:ok |
Keyword :ok | No |
"hi" |
String "hi" | No |
[a b] |
2-element tuple | a, b |
[h & t] |
Non-empty tuple | h (head), t (tail) |
{a b c} |
3-element vector | a, b, c |
%{:k v} |
Map with key :k |
v |
#{a b} |
2-element set | a, b |
(pid r l) |
PID | r (realm), l (local) |
#bytes[0x89 & r] |
Binary prefix | r (rest) |
#bits[v:4 & _] |
Bit fields | v |
Variable binding:
Wildcard (no binding):
Literal matching:
(match 42
42 :forty-two
_ :other) ; => :forty-two
(match :ok
:ok :success
:error :failure) ; => :success
(match "hello"
"hello" :greeting
_ :other) ; => :greeting @todo
Tuple patterns:
(match [1 2]
[a b] (+ a b)) ; => 3
(match [:ok 42]
[:ok val] val
[:error _] nil) ; => 42
(match [:error :not-found]
[:ok val] val
[:error reason] reason) ; => :not-found
Rest patterns in tuples:
Vector patterns:
(match {1 2 3}
{a b c} (+ a b c)) ; => 6 @todo
(match {10 20}
{x y} (* x y)) ; => 200
(match {}
{} :empty
_ :not-empty) ; => :empty
Map patterns:
(match %{:name "Alice" :age 30}
%{:name n} n) ; => "Alice"
(match %{:a 1 :b 2}
%{:a x :b y} (+ x y)) ; => 3
(match %{:a 1}
%{:b x} :has-b
_ :no-b) ; => :no-b
Set patterns:
PID patterns:
;; @todo
(def my-pid (self))
(match my-pid
(pid realm local) [realm local]) ; => [<realm-id> <local-id>]
Binary patterns:
;; @todo
(match #bytes[0x89 0x50 0x4E 0x47]
#bytes[0x89 & rest] rest) ; => #bytes[0x50 0x4E 0x47]
(match #bytes[1 2 3]
#bytes[a b c] (+ a b c)) ; => 6
Bit field patterns:
;; @todo
;; Extract fields from a byte using bit patterns
(match #bytes[0x45]
#bits[version:4 ihl:4] [version ihl]) ; => [4 5]
;; Endianness in bit patterns
(match #bytes[0x00 0x50]
#bits[port:16/be] port) ; => 80
(match #bytes[0x50 0x00]
#bits[port:16/le] port) ; => 80
Multiple clauses (first match wins):
Guards¶
(match 5
n when (> n 0) :positive
n when (< n 0) :negative
_ :zero) ; => :positive
(match -3
n when (> n 0) :positive
n when (< n 0) :negative
_ :zero) ; => :negative
(match 0
n when (> n 0) :positive
n when (< n 0) :negative
_ :zero) ; => :zero
Guards must be pure expressions.
No match exits process:
;; When no pattern matches, process exits with badmatch
(match 5
1 :one
2 :two) ; => ERROR :badmatch
do¶
Evaluates expressions in sequence, returns last.
Semantics: - Evaluates each expression left-to-right - Returns value of last expression - Used for side effects
Returns last expression:
Single expression:
Empty do:
Side effects execute in order:
;; @todo
(def result {})
(do
(def result (append result 1))
(def result (append result 2))
(def result (append result 3))
result) ; => {1 2 3}
quote¶
Prevents evaluation, returns form as data.
Semantics: - Returns the form unevaluated - Symbols remain symbols, lists remain lists
Quoting symbols:
Quoting lists:
Quoting prevents evaluation:
Quoting different types:
Nested quotes:
Quoting special forms:
;; Special forms can be quoted like any symbol
'def ; => def
'fn* ; => fn*
'match ; => match
'do ; => do
'quote ; => quote
(symbol? 'def) ; => true
Macro Definition¶
Macros are not a special form. A macro is a var with %{:macro true} metadata:
The compiler invokes the function at compile-time, passing unevaluated forms.
Macro expansion: