Koskari and s-expressions
Koskari is the layout language: indentation carries structure, and parentheses carry expressions. Every layout form reads to the same list structure that a parenthesized s-expression produces, so the compiler, the macro expander, and the evaluator never see which spelling was used. Layout reduces the reliance on parentheses for structure; it does not remove them.
The rules below are the whole surface. Each is shown as a pair: the layout spelling on the left, and on the right the s-expression the reader produces from it. The normative statement of the rules is Reader and layout syntax.
Lines
A line with one element is that value. A line with more than one element is a form headed by the first, which is a call, a definition, or any other form the head names.
Koskari
print "hello" name
name
def sq [x] (* x x)
s-expression
(print "hello" name)
name
(def sq [x] (* x x))
Blocks
A line ending in : opens a block. The header’s elements and the body’s
lines join into one form, and each body line follows the line rule on its
own. The head names a layout handler, layout/<head>; with no handler
registered, the default splices body forms after the header.
Koskari
when (> n 0):
print n
n
s-expression
(when (> n 0) (print n) n)
Named heads and peers
Some heads have their own handler, which may gather peer blocks at the same
indent and lower the whole group to one form. Block if gathers elif and
else and reads as cond.
Koskari
if (< x 0):
"negative"
elif (== x 0):
"zero"
else:
"positive"
s-expression
(cond
[(< x 0) "negative"]
[(== x 0) "zero"]
[else: "positive"])
The handlers and their peers are catalogued in the layout handler reference.
Keywords
A line that opens with a keyword is an argument name and its value. One
form after the keyword is the value; several fold to a call. A keyword
followed by : takes a block as its value, and a bare k:: takes a
begin block.
Koskari
print:
"hello"
file: stderr
end: ""
s-expression
(print "hello" file: stderr end: "")
Parentheses inside layout
Any (, [, or { switches to s-expression rules until it closes, at any
depth. A newline inside an open delimiter does not end the line, so a
header argument may span lines.
Koskari
let [[a 1]
[b 2]]:
+ a b
s-expression
(let [[a 1] [b 2]]
(+ a b))
Side by side
Koskari
def greet [name]:
print f"hello {name}"
name
s-expression
(def greet [name]
(print f"hello {name}")
name)
Koskari
for-each [item items]:
print item
s-expression
(for-each [item items]
(print item))
Koskari
while (running? x):
step x
else:
"done"
s-expression
(while (running? x)
(step x)
(else: "done"))
Koskari
try:
risky x
except [e ValueError]:
print e
finally:
cleanup
s-expression
(try (begin (risky x))
[[e ValueError] (print e)]
[finally: cleanup])
Koskari
class Point (Base):
var x 0
s-expression
(class Point (Base)
(begin (var x 0)))
Koskari
render:
#[1 2 3]
sep: ", "
s-expression
(render #[1 2 3] sep: ", ")
Where parentheses stay
Four places keep their delimiters inside layout:
Nested expressions, such as
(< x 0)in a header.Inner calls on a line, such as
print (sign -3).Formals and bindings, such as
[name]and[[a 1] [b 2]].Collection literals, such as
#[1 2 3].
Statement-shaped code takes layout; expression-shaped code takes parentheses. A whole program written with parentheses only is valid Koskari, and the REPL accepts both spellings.
See also
Reader and layout syntax, the normative block, line, colon, and peer rules
Layout handler reference, per-head lowering
Layout and calls, tutorial patterns in
.kifiles