Lake

Error handling

Lake has no exceptions and no null. Fallible operations return a value that encodes success or failure, and unrecoverable bugs abort loudly with panic.

§Option — a value or nothing

Option[T] is the standard enum for “maybe a value”:

lake
+std.option.{ Option is_some unwrap_or }

# Option[T] is enum { Some(T)  None }

Instead of returning a null pointer, a lookup returns Some(v) or None, and the caller must handle both — checked by when exhaustiveness:

lake
describe is {
  o Option[i64] -> ret buf {
    when o {
      Some(v) -> { ret "present" }
      None    -> { ret "absent" }
    }
  }
}

unwrap_or(o d) extracts the value or falls back to d.

§Result — success or failure

Result[T E] carries an error value on the failure path:

lake
+std.result.{ Result is_ok }

# Result[T E] is enum { Ok(T)  Err(E) }

handle is {
  r Result[i64 buf] -> ret i64 {
    when r {
      Ok(v)  -> { ret v }
      Err(e) -> { ret 0 - 1 }
    }
  }
}

Because Ok and Err are variants, the compiler forces you to address the error case — there is no way to “forget” to check.

§panic — for the unrecoverable

When a bug means the program cannot sensibly continue, panic prints a message with the call-site location and aborts:

lake
+std.panic.{ panic assert }

main is {
  _ -> {
    panic("invariant violated")
  }
}
lake: panicked at src/main.lake:4:5: invariant violated

assert(cond msg) is the everyday guard — it panics with msg unless cond holds:

lake
pin assert(i < n "index out of range")

panic is the Rust-style escape hatch: use Option/Result for expected failure, panic/assert for “this should never happen”.

§Key ideas

  • No exceptions, no null: failure is a value (Option[T], Result[T E]).
  • when exhaustiveness forces every case to be handled.
  • panic(msg) aborts with a file:line:col location; assert(cond msg) guards invariants.