Optionals
T? is a T that may not be there, and none is the value that is not. It is the answer whenever "there is nothing here" is the whole story, with no reason worth carrying.
func main():
print(f"{parse_int("42") else -1}")
print(f"{parse_int("nonsense") else -1}")
print(f"{parse_float("2.5") else 0.0}")42 -1 2.5
Narrowing#
After a test the name is its payload. There is no unwrap operator.
func classify(text: String) -> String:
let n = parse_int(text)
if n == none:
return f"{text} is not a number"
if n < 0:
return f"{text} is negative"
return f"{text} doubles to {n * 2}"
func main():
print(classify("21"))
print(classify("-3"))
print(classify("x"))21 doubles to 42 -3 is negative x is not a number
An early-exit guard narrows everything below it, which is the shape most real code takes.
func main():
let raw = arg(0)
let n = parse_int(raw)
if n == none:
print(f"not a number: {raw}")
return
print(f"{n} squared is {n * n}") # n is Int from here down17 squared is 289
else, chained#
else associates to the right, so a chain of fallbacks reads left to right and stops at the first one that is there.
func main():
let first = parse_int("x") else parse_int("y") else parse_int("3") else 0
print(str(first))
let n = parse_int("10")
print(str(n else 0 > 5)) # else binds tighter than comparison
print(str((n else 0) + 1))3 true 11
Optionals as parameters and fields#
struct Setting:
name: String
limit: Int?
func describe(setting: Setting) -> String:
let limit = setting.limit
if limit == none:
return f"{setting.name}: unlimited"
return f"{setting.name}: {limit}"
func main():
print(describe(Setting(name = "retries", limit = 3)))
print(describe(Setting(name = "size", limit = none)))retries: 3 size: unlimited
Narrowing works on locals and parameters, never on a field or an element — those could change between the test and the use. Bind the field to a name and test that, which is what describe does above.
The assert-unwrap#
There is no force-unwrap sigil. x else trap("…") says the same thing and is greppable.
func main():
let config = "port=notanumber"
let port = parse_int(config[5:len(config)]) else trap("bad port in config")
print(str(port))loom: trap: bad port in config [explicit_trap]
at main (main.luc:3:5)