LLuce

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.

main.luc
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}")
Output
42
-1
2.5

Narrowing#

After a test the name is its payload. There is no unwrap operator.

main.luc
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"))
Output
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.

main.luc
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 down
Output
17 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.

main.luc
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))
Output
3
true
11

Optionals as parameters and fields#

main.luc
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)))
Output
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.

main.luc
func main():
    let config = "port=notanumber"
    let port = parse_int(config[5:len(config)]) else trap("bad port in config")
    print(str(port))
Output — the program traps
loom: trap: bad port in config [explicit_trap]
    at main (main.luc:3:5)