Chapter 9 — Errors and Optionals

Ingle has no exceptions and no null. Nothing is ever secretly absent, and nothing throws past you up an invisible staircase of stack frames. When something can fail or be missing, that fact is written into the type, and you deal with it where it happens.

Two enums carry this, and — this is the lovely part — they’re not special built-ins. They’re ordinary generic enums (Chapter 10) that happen to be so useful they’re provided to every program automatically:

  • Option<T> is Some(value) or None — “a T, or nothing.”
  • Result<T, E> is Ok(value) or Err(error) — “a T, or an error of type E.”

Option: maybe there’s a value

fn safe_div(a: int, b: int) -> Option<int> {
    if b == 0 { return None }
    return Some(a / b)
}

fn main() -> int {
    match safe_div(10, 2) {
        case Some(v) { return v }   // v is the int, 5
        case None    { return 0 }
    }
}
=> 5

There’s no way to “forget to check.” To get the value out, you match, and the compiler makes you handle the None. The entire family of null-pointer bugs is structurally impossible.

Result: it worked, or here’s why it didn’t

fn checked(n: int) -> Result<int, string> {
    if n < 0 { return Err("negative") }
    return Ok(n)
}

Result is Option with a reason attached. When something fails, you don’t just learn that it failed; you get an error value explaining it.

The ? operator: stop the boilerplate

Checking every Result by hand gets tedious fast — the dreaded pyramid of “if this failed, return the failure.” The ? operator collapses it. Put ? after an expression that yields a Result (or an Option): if it’s Ok/Some, you get the value and carry on; if it’s Err/None, the whole function returns that failure immediately.

fn sum(a: int, b: int) -> Result<int, string> {
    return Ok(checked(a)? + checked(b)?)   // any Err here short-circuits the whole function
}

fn main() -> int {
    match sum(10, 20) {
        case Ok(v)  { println("ok {v}")  return v }
        case Err(e) { println("err {e}") return -1 }
    }
}
ok 30
=> 30

The catch — and it’s a sensible one — is that ? can only propagate a failure your function is declared to return. Use ? on a Result<_, string> and your function must itself return a Result with that same string error type; use it on an Option and your function must return an Option. The failure always has somewhere legal to go.

Fireside trivia. Tony Hoare introduced the null reference in 1965 and later called it his “billion-dollar mistake,” estimating the cost of the bugs, crashes, and security holes it caused over the following decades. He was almost certainly low-balling it. Ingle is one of a growing number of languages built by people who took that confession seriously and simply declined to include the feature.