Data and control
Values and control flow are where Python habits carry the furthest.
Atoms
Python
42
-3
"hello"
True, False, None
Koskari
42
-3
"hello"
True False None
What is nil?
The empty Lisp list, the end of every pair chain. It is not None:
(nil? nil) is true and (None? nil) is false. Python data never
produces one; you meet it with quoted lists and macros.
Collections
Python
[1, 2, 3]
(1, 2, 3)
{"a": 1, "b": 2}
Koskari
#[1 2 3]
#(1 2 3)
#{"a": 1 "b": 2}
Why the #?
Bare [1 2 3] reads like (1 2 3), a call of 1. The # marks a
literal. Keys in #{...} are expressions, so a string key is quoted; a
bare a: key would look up the variable a.
Python
[*xs, 1, 2]
(1, *xs)
Koskari
#[*: xs 1 2]
#(1 *: xs)
Python
xs[0]
xs[-1]
d["a"]
xs[1:]
Koskari
xs[0]
xs[-1]
d["a"]
(item-slice xs 1)
Assignment
Python
x = 6
x = 7
x += 1
n -= 2
Koskari
var x 6
set! x 7
incr! x
decr! n 2
Why two words for =?
var introduces a name in the current scope. set! stores into a
name that exists; inside a function, a set! to a name bound nowhere
is a compile error that names the fix. Python lets one symbol do both
and infers which you meant.
Python
obj.count = 0
d["k"] = v
Koskari
set! obj.count 0
set! d["k"] v
Conditionals
Python
if x < 0:
s = "negative"
elif x == 0:
s = "zero"
else:
s = "positive"
Koskari
var s:
if (< x 0):
"negative"
elif (== x 0):
"zero"
else:
"positive"
Why does the var go outside?
An if is an expression. Its value is the value of the branch that
ran, so one var takes the whole choice. Python’s if is a
statement, so each branch assigns.
Python
def invite(user):
if not user:
return "missing user"
if not user.email:
return "missing email"
return f"sent to {user.email}"
Koskari
def invite [user]:
unless user:
return "missing user"
unless user.email:
return "missing email"
f"sent to {user.email}"
What are when and unless?
One-armed if. when runs its body if the test is true, unless if
it is false. They keep a guard clause positive instead of writing
(not ...).
Loops
Python
while n > 0:
n -= 1
else:
print("done")
Koskari
while (> n 0):
decr! n
else:
print "done"
Python
for item in seq:
if item.ok:
continue
process(item)
Koskari
for-each [item seq]:
when item.ok:
continue
(process item)
Does for-each have else and break?
Both, as in Python: the else: peer runs when the loop ends without a
break. break may carry a value, which becomes the value of the
loop:
var found:
for-each [v values]:
when (> v 2):
break v
else:
None
Python
a and b
a or b
not a
Koskari
(and a b)
(or a b)
(not a)
Run it
Save as countdown.ki:
def countdown [n]:
while (> n 0):
print n
decr! n
else:
print "lift-off"
@entry-point
def main []:
(countdown 3)
3
2
1
lift-off