SCUA branches with if and loops with while
and for. Loops walk collections directly, and you count
with ranges written a:b. Every block closes with
end.
fn describe(n)
if n < 0 then
return "negative"
elseif n == 0 then
return "zero"
else
return "positive"
end
end
print(describe(-4))
print(describe(0))
print(describe(7))
Run it:
$ scua describe.scua
negative
zero
positive
#if, elseif, else
An if tests a condition after then and runs
the branch if the condition is true (see truthiness for what counts as
true). You can chain alternatives with elseif and provide a
fallback with else. The whole thing closes with
end. Both elseif and else are
optional.
#Conditional expressions
The same if … then … else … end can also produce a
value, so a conditional can go anywhere an expression goes — into a
binding, a return, a call argument, a string hole. There's
no separate ternary operator to learn; the if you already
know is the ternary.
let hp = 0
let status = if hp > 0 then "alive" else "dead" end
print(status)
print(if hp < 0 then -hp else hp end) -- an inline "abs", no temporary needed
$ scua status.scua
dead
0
When an if is used as a value, the else is
required — every path has to produce something, so
there's never a silent nil. (As a plain statement, where
you're branching for effect rather than for a value, else
stays optional.) elseif chains work just as they do in the
statement form:
fn classify(n)
return if n < 0 then "negative"
elseif n == 0 then "zero"
else "positive" end
end
print(classify(-4))
print(classify(0))
print(classify(7))
$ scua classify.scua
negative
zero
positive
match is an expression too. Each arm is a single
expression, and the match evaluates to the matched arm's value — handy
for mapping one value to another:
fn http_label(code)
return match code
200 -> "OK"
404 -> "Not Found"
_ -> "Other"
end
end
print(http_label(200))
print(http_label(404))
print(http_label(500))
$ scua http.scua
OK
Not Found
Other
A match expression over a closed
enum is still exhaustiveness-checked, so a forgotten case is a
compile error rather than a fallthrough. See Pattern matching for the full set of arm
shapes — they all work in the value form, including payload binds and
when guards.
Note: avoid the old
cond and a or btrick for this. It quietly returns the wrong value whenais itselffalseornil(both common in game data) — a value-producingifhas no such trap.
#while
A while loop runs its body, between do and
end, as long as the condition holds:
let n = 1
while n < 100 do
n = n * 2
end
print(n)
Run it:
$ scua while.scua
128
#for over a collection
for v in collection do ... end walks a collection and
binds each element to v in turn. Over an array you get the
elements:
let colors = ["red", "green", "blue"]
for c in colors do
print(c)
end
Run it:
$ scua for-values.scua
red
green
blue
Add a second loop variable to get the index alongside each value. Indices are 0-based:
let colors = ["red", "green", "blue"]
for i, c in colors do
print(`{i}: {c}`)
end
Run it:
$ scua for-index.scua
0: red
1: green
2: blue
Tables iterate the same way. With one variable you get each value; with two you get the key and the value. See Collections for tables.
#Ranges: a:b
A range a:b counts from a up to but not
including b. It's half-open, so 0:n gives
exactly n values, 0 through n-1.
This pairs cleanly with 0-based indexing.
for i in 0:5 do print(i) end
Run it:
$ scua range.scua
0
1
2
3
4
A range counts as it goes rather than building an array first, so
looping over 0:n costs nothing per element beyond the loop
itself. (If you do want the numbers as an actual array, the
range(a, b) builtin gives you one; see Collections.)
A third component sets the step. 0:10:2 counts up by
2:
for i in 0:10:2 do print(i) end
Run it:
$ scua step.scua
0
2
4
6
8
To count down, call .reversed() on a range. It walks the
same half-open set backwards, so the low bound is included and the high
bound is not. That avoids the off-by-one you'd get from a bare negative
step:
for t in (1:6).reversed() do print(t) end
print("go!")
Run it:
$ scua countdown.scua
5
4
3
2
1
go!
#break and continue
break exits the nearest enclosing loop immediately. Here
it stops at the first multiple of 7 at or above 21:
let hit = 0
for k in 21:100 do
if k % 7 == 0 then
hit = k
break
end
end
print(hit)
Run it:
$ scua break.scua
21
continue skips to the next iteration without running the
rest of the body:
for k in 0:10 do
if k % 2 == 0 then continue end
print(k)
end
Run it:
$ scua continue.scua
1
3
5
7
9
In nested loops, break and continue apply
to the innermost loop only.