Error Handling
Sometimes a method can't guarantee that it will be successful. The File.openInputStream()
method,
for example, might fail because the file does not exist, or because of permission problems, or for
any of a number of other reasons.
There are three main ways for a method to communicate failures back to its caller:
- A nullable result. This is the right choice when there is no need to convey any additional
information back to the caller beyond "it failed". For instance, when the
Int64.parse
function cannot parse a string, it simply returnsnull
to indicate that "this wasn't a valid number". There really isn't any need for further information, so the function doesn't bother with theError
class at all. - A
Maybe
result. TheMaybe<T>
type contains either an instance of the typeT
, or anError
. The caller can inspect this object to retrieve the result or examine theError
. - The return type
Error?
. When a method does not normally need to return any information, but still might fail,Error?
is the right return type.File.delete()
is a good example of this sort of method. It is a compile-time error for a program to call a method which returnsError?
and ignore the return value.
Because it is common to call many Error?
-returning methods in sequence (for instance, interacting
with the filesystem will tend to involve many such calls), manually checking the result of each call
quickly becomes tedious. The try ... fail
syntax exists to help with this:
try {
def out := path.openOutputStream()
out.write("No error handling necessary here!")
}
fail(e) {
Console.printLine("Error writing file: \{e}")
}
Within a try
block, if any method call results in an error (either a Maybe
result which contains
an Error
or a non-null
Error?
return value), the program immediately jumps to the fail
block. Since all Maybe
s within the try
block are known to have succeeded (otherwise, we would
have jumped to the fail
block), they are automatically unwrapped. The variable out
in the above
snippet of code has type OutputStream
, despite the fact that path.openOutputStream()
returns
Maybe<OutputStream>
- placing the call inside of a try
block handles the error checking and
unwrapping automatically.
Note that Frost's try
block is very different from try
blocks in languages such as C++ and Java.
Frost does not feature exception handling and Frost's try
therefore has nothing to do with
exceptions. Frost Error
s do not cause the stack to unwind, will not propagate to enclosing
contexts, etc. Frost's try
block simply forwards any Errors
happening within it to the fail
block and automatically unwraps any Maybes
.