Error handling
The Result type
Result[T, E] is Cleat's primary error handling mechanism. There are no exceptions.
cleat
let ok: Result[int, string] = Ok(42) let err: Result[int, string] = Err("not found")
Functions that can fail return Result:
cleat
fn parse_config(path: string) -> Result[Config, string] needs { fs } { let text = fs.read(path)? let config: Config = json.decode(text)? return Ok(config) }
The ? operator
? unwraps an Ok value. If the Result is Err, it returns immediately from the enclosing function with that error.
cleat
fn load_and_process() -> Result[int, string] needs { fs } { let text = fs.read("data.txt")? // if Err, return Err immediately let lines = fs.read_lines("data.txt")? // same — early return on error return Ok(lines.len()) }
The enclosing function must return Result to use ?. The error type must be compatible.
Combining with pattern matching
For finer control, use match:
cleat
let data = match load_config() { Ok(config) => config.data, Err(e) => { log.warn("config failed: ${e}, using defaults") default_data() }, }
Error propagation chains
Errors bubble up naturally through ?:
cleat
fn fetch_and_decode(url: string) -> Result[Response, string] needs { net } { let resp: http.Response = http.get(url)? // network error → Err let data: Response = json.decode(resp.body)? // parse error → Err return Ok(data) } fn process(url: string) -> Result[int, string] needs { net } { let response = fetch_and_decode(url)? // propagates either error return Ok(response.count) }
let _ = expr?
Discard the success value while still propagating errors:
cleat
fn setup() -> Result[int, string] needs { fs } { let _ = fs.write("config.json", default_config)? // propagate write errors return Ok(0) }