Skip to content
Kavachकवच

try

● Intermediate4 minBefore this: ErrorsSpec 5.2

try passes an error on to your own caller: if the call fails, your function fails with the same error.

type AgeError = Empty | NotANumber(text: String)

fn parse_age(text: String) -> Int ! AgeError {
    if text.is_empty() {
        fail AgeError.Empty
    }
    Int(text) catch _ => fail AgeError.NotANumber(text: text)
}

fn total_age(a: String, b: String) -> Int ! AgeError {
    const first = try parse_age(a)
    const second = try parse_age(b)
    first + second
}

fn main() {
    print(total_age("31", "27") catch -1)
    const bad = total_age("31", "x") catch e => {
        print("failed: {e}")
        -1
    }
    print(bad)
}

The program ran and printed:

kavach run try.kav

58failed: NotANumber(text: "x")-1
try.kav: Kavach code, then what running it prints

total_age calls parse_age twice. Each try means: if this fails, stop here and give the error to my caller. The happy path reads straight down, with no checks in between.

try only works in a function that can fail with that error itself: here, both are ! AgeError.

Methods that take a function that can fail

A built-in method that takes a function (map, filter, sum, sort_by, parallel_map, Shared.update, …) passes that function's error on. Given a function that can fail, the call can fail with its error, and the method stops at the first one:

type PriceError = Unknown(item: String)

fn price(item: String) -> Int ! PriceError {
    match item {
        "pen" => 10,
        "cup" => 25,
        _ => fail PriceError.Unknown(item: item),
    }
}

fn total(items: [String]) -> Int ! PriceError {
    const prices = try items.map(price)
    prices.sum(p => p)
}

fn main() {
    print(total(["pen", "cup"]) catch _ => 0)
    const stopped = total(["pen", "kite", "cup"]) catch e => match e {
        PriceError.Unknown(item) => {
            print("no price for {item}")
            0
        },
    }
    print(stopped)
    print(["pen", "kite", "cup"].sum(i => price(i) catch _ => 0))
}

The program ran and printed:

kavach run methods-pass-on.kav

35no price for kite035
methods-pass-on.kav: Kavach code, then what running it prints
  • The map call is the one that can fail, so it is the one marked, with try or catch.
  • A lambda that handles the error inside, as in the sum, can't fail, and neither can the call.
  • Shared.update given a function that fails leaves the shared value as it was.