Exceptions
Exceptions allow a program to transfer control from the point of error to a designated exception handler, separating error-handling logic from regular program flow.
Throwing exceptions
Exceptions are thrown using the  throw  function. Example:
grabOption[T](option: Option[T]): T {
    | Some(v) => v
    | None => throw(GrabException())
}
In this example, if the argument is  Some(v) , then  v  is returned. Otherwise, a  GrabException  is thrown.
Any type declared with the  data  or  newtype  keyword can be thrown as an exception, since the type will have an instance for the  HasAnyTag  trait.
Catching exceptions
When an exception is thrown, it propagates up the call chain to the nearest  try , where it can be caught:
try {
    grabOption(None)
} catch {| GrabException, error =>
    Log.trace("A GrabException occurred")
    error.rethrow()
}
The first argument passed to  catch  is the thrown value, and the second parameter contains the stack trace.
The  rethrow()  method throws the exception again without altering the stack trace.
It is used when the exception is caught where it can't be fully handled.
If the exception reaches the top of the call stack, the exception value and the stack trace will be printed.
Catching any exception
It is also posssible to catch any exception, regardless of its type, using the  catchAny  method:
try {
    let result = fragileOperation()
    Some(result)
} catchAny {error =>
    None
}
Cleaning up
Cleanup often needs to happen whether or not an exception is thrown.
This is what the  finally  method guarantees:
let fileHandle = path.readHandle()
try {
    process(fileHandle)
} finally {
    fileHandle.close()
}
If the program is terminated abnormally (force closed, power is lost, etc.) there is no guarantee that  finally  will get called.
In most environments, the operating system will occasionally terminate the program abnormally, so programs should be designed such that they can recover from abnormal termination.
Catching multiple exceptions
The  try  function returns a value of type  Try[T]  that is defined as follows:
data Try[T] {
    Success(value: T)
    Failure(error: Error)
}
The  catch ,  catchAny  and  finally  methods are defined on this type. However, since they don't return a  Try[T]  value, they can't be chained.
However, the alternative  tryCatch ,  tryCatchAny  and  tryFinally  methods do return a  Try[T]  value, and can thus be chained:
try {
    doSomething()
} tryCatch {| GrabException, error =>
    reportSpecificError()
} tryCatchAny {error =>
    reportGeneralError()
} finally {
    performCleanup()
}
The last method in the chain here is  finally . If it was  tryFinally , a value of type  Try[T]  would be returned.
