Hare lets you define your own error types and leverage tagged unions to define a generic module error of all possible errors to make handling them easier. Errors are defined like any other type, but are prefixed with the ! to denote they are an error type. Errors can be any built-in type, but most times they are `!void` which merely defines a type with no size at all.
use errors;
use fmt;
// Something got flubbed error
type flubbed = !void;
// Something derped error
type derpped = !void;
// This error has data associated with it that is a size so we can
// return an error with some context.
type invalid = !f64;
// Tagged union of all possible errors along with our custom errors
// so we can use this error in our functions returns.
type error = !(...errors::error | flubbed | derpped | invalid);
// It is good practice to offer a strerror() function that matching on
// all of your possible error type and returns a human-friendly version
// of it. Since we also have the tagged union with errors::error we
// default to that module's strerror() function to handle those strings.
fn strerror(err: error) str = {
match (err) {
case flubbed =>
return "flubbed occured";
case derpped =>
return "derpped occured";
// Matching our our invalid errors lets us unpack the data
// inside and cast it to its internal type to get the value of
// what invalid has.
case let i: invalid =>
return fmt::asprintf("invalid: {}", i: f64)!;
case let err: errors::error =>
return errors::strerror(err);
};
};
// If a is less than 0 then this returns a flubbed error.
fn add(a: int, b: int) (int | flubbed) = {
if (a < 0) {
return flubbed;
};
return a + b;
};
// If b is less than 0 then this returns a derpped error.
fn mul(a: int, b: int) (int | derpped) = {
if (b < 0) {
return derpped;
};
return a * b;
};
fn div(a: f64, b: f64) (f64 | invalid) = {
if (b < 0f64) {
return b: invalid;
};
return a / b;
};
// calc attempts to add and mul the args and propgate any error up.
// Since our local error type is in the return which can be flubbed or
// derpped this is allowed.
fn calc(a: int, b: int) (int | error) = {
let a = add(a, b)?;
let m = mul(a, b)?;
return a + b;
};
export fn main() void = {
// We know this will not error so we can assert ! just fine
let a = add(1, 1)!;
fmt::println(a)!;
// We know this will not error so we can assert ! just fine
let m = mul(5, 5)!;
fmt::println(m)!;
// Since -1 will cause add() to error with flubbed we'll catch
// this in the first case even though calc returns the general
// error type.
let c = match (calc(-1, 100)) {
case let err: flubbed =>
fmt::printf("our error str: {}\n", strerror(err))!;
case let err: derpped =>
fmt::printf("our error str: {}\n", strerror(err))!;
case let result: int =>
yield result;
};
let d = match (div(10f64, -2f64)) {
case let err: invalid =>
fmt::printf("our error str: {}", strerror(err))!;
case let result: f64 =>
yield result;
};
};
$ hare run custom-errors.ha
4/4 tasks completed (100%)
2
25
our error str: flubbed occured
our error str: invalid: -2
Back to table of contents