Brackets and the call convention
Koskari reads ( ) and [ ] with the same grammar. A [ ] form is not a
vector, an array, or a list literal. Both read into pair chains, and by
default both compile the same way.
Same grammar, same compilation
A non-empty list in head position compiles to a call, whichever bracket delimited it:
(f 1 2) ; call f with 1 and 2
[f 1 2] ; the same call
[a 1] is a call to a with argument 1. A reader arriving from a
language where [...] builds a list has this one thing to unlearn.
Collection values are the #(...), #[...], and #{...} reader forms:
tuple, list, dict.
What the brackets carry
The reader records that a form came from [ ] by producing a
bracket-pair, and bracket-nil for the empty []. A bracket-pair is
value-equal to a pair, every pair operation works on it, and it
remembers its bracket origin. That memory does nothing on its own; it is
there for tooling and for the compiler’s call-position check.
The convention
By default the compiler points the convention out rather than enforcing
it, with a SyntaxWarning and no change to compilation:
( )is a call. The first element is applied to the rest. Some forms evaluate every argument; others treat certain arguments as data.[ ]is a data fragment that the enclosing form consumes: a binding list, acondclause, a pattern row.
The convention is a reading aid first. Nobody reads
(let [[a 1]] (+ a 100))
as a call of a with 1; the brackets say “binding” at a glance. A
binding list is consumed as data by let and never reaches the call
compiler, whatever the convention says.
Catching a typo
Because brackets mark data positions, a bracketed fragment that reaches
call position is a sign of a mistake. With ler typed for let:
(ler [[a 1]] (+ a 100))
ler is not a special form, so the form compiles as a call of the value
ler whose first argument is [[a 1]], that is, (call (call a 1)).
The bracket marker lets the compiler see a [ ] fragment in call
position and say so.
Changing the behaviour
Two settings, changed only by a pragma near the top of a
module; there is no global switch and no per-form syntax:
Setting |
Affects |
Values |
Default |
Meaning |
|---|---|---|---|---|
|
reader |
on, off |
on |
whether |
|
compiler |
|
|
what happens when a bracket-pair reaches call position |
;; a [ ] fragment in call position is a compile error
(pragma [compiler/call-brackets 'error])
;; the check is off; brackets are documentation
(pragma [compiler/call-brackets 'ignore])
;; no marker is produced, so no check can fire
(pragma [reader/brackets False])
With error, the ler typo above is caught at compile time. A pragma
is forward-only and scoped to its module: it affects the code after it
and never the modules it imports.
See also
Koskari and s-expressions: layout beside the s-expressions the brackets appear in.
Grammar: the reader grammar for lists and brackets.