module Exceptions {
// EXCEPTION BASICS
fn test() {
// any expression can throw exceptions
let x = 1 / 0; // this expression will throw due to integer divide by zero
// to handle an exception, use try, which returns a Result{T}, where T is
// the type of the expression (e.g. int below).
let r = try { 1 / 0 }
// Result is a union with two cases: a value and an exception.
//
// union Result{T} {
// val : T;
// ex : Except;
// }
//
// Use .ex? and .val? to check to see if the result contains an exception
// or a value.
let has_exception = r.ex?; // true if result has exception
let has_value = r.val?; // true if result has a value
// Use .ex and .val to obtain the exception or value
if (r.ex?) {
print("Uh oh: ", r.ex);
} else {
print("It worked! ", r.val);
}
}
// DEFINING EXCEPTION TYPES
// The Except type is an extensible union, which allows it's contents to be defined elsewhere.
//
// union Except {
// ... // extensible
// }
//
// Use except to define types that can be thrown. These types will be added to the Except union.
except {
SomewhatBad{} // defines new struct with no fields
ReallyBad{x : double} // defines new struct with field x
}
fn test2() {
let x = rand() * 10; // generate random number between 0..10
// use throw to raise an exception with a value
// the type of the value thrown must be defined with except
let r = try {
if (x < 1) {
throw SomewhatBad; // raises an exception with SomewhatBad type
}
if (x > 5.0) {
throw ReallyBad(x); // raises an exception with an instance of ReallyBad, with a value
}
x + 1; // non-exception value
}
// is can be used to check the exception type
if (r.ex? && r.ex is SomewhatBad) {
// SomewhatBad exception was raised
}
// match can be used to match and extract the value and the exception value
match (r) {
Result(val : @val) => print("It worked! ", val); // print value
Result(ex : SomewhatBad) => print("somewhat bad"); // handle SomewhatBad exception
Result(ex : ReallyBad(@val)) => print("really bad: ", val); // handle ReallyBad exception
}
// alternatively nested match can be used for the exception types
match (r) {
Result(val : @val) => print("It worked! ", val); // print value
Result(ex : @ex) => match (ex) { // nested match
SomewhatBad => print("somewhat bad"); // handle SomewhatBad exception
ReallyBad(@val) => print("really bad: ", val); // handle ReallyBad exception
}
}
// result is a "truthy" value meaning it can be used in conditional expressions
// (it's true when there is a value, and false if there is an exception)
// result also supports ! which extracts the value (same as using .val)
if (r) { // same as if (r.val?)
print("val", r!) // same as print("val", r.val)
} else {
print("ex", r.ex)
}
}
// except can be used to add exception types defined elsewhere
// e.g. they do not need to be defined within the except definition
struct SizeMustBePositive {size: double}
// allow SizeMustBePositive, string and int to be thrown
except SizeMustBePositive | string | int;
}