1. Relation between exception and option type
We have seen in the chapter Basic programming
concept,
that the Ymir language have a primitive option type. This type has a
really close relation with exceptions. Indeed, any value can be store
inside an option type, and any failing scope can be used to create an
empty option value. The token ?
is used to transform a value
into an option type.
In the following example, the foo
function can throw an
exception. The main
function calls it, but use enclose the
result inside an option type, to handle the errors. The type of the
variable x
is then (i32)?
, meaning option of i32
. A
pattern matching, is then used to check wether the x
contains a
value or not. In this example, a new pattern of the pattern matching
is presented. This pattern is specific to the case of option type, and
adds two keywords Ok
, and error Err
. They can be used with
or without internal pattern (never named), to get the content of the
option (in case of Ok
), or the exception (in case of Err
).
import std::io;
def foo (i : i32)-> i32
throws &AssertError
{
assert (i < 10, "i must be lower than 10");
i + 12
}
def main () {
let x = foo (10) ?
match x {
Ok (y : _) => {
println ("x contains the value : ", y);
}
Err (err : _) => {
println ("x has no value, because : ", err);
}
}
}
Results (in release mode) :
x has no value, because : core::exception::AssertError (i must be lower than 10)
One can note from the previous example, that the main
function
is safe. The option enclosing catches every exceptions. The exception
can then be retreived by using a pattern matching, as presented above.
Warning Unfortunately, the accuracy of the exception type that is
thrown is lost at compile time, (i.e. the type contained inside an
option type is Exception
). Even, if the type can be specifically
retreived at execution time. To remove this limitation, the different
type of exception would have to be written in the definition of the
option type. This would be extremely verbose. Contribution Maybe
adding the possibility to write it, but optionaly, in order to store
it if possible. I don't know if it is a good idea.
1.1. Empty option without exception
It is possible to initialize an empty option value without throwing an
exception. This can be done using the type specific attribute
err
. In that case, the option does not have a value, even if it
is an Err
value.
import std::io;
def foo ()-> i32? {
(i32?)::__err__
}
def main () {
match foo () {
Err (err : _) => {
println ("Empty but with exception : ", err);
}
Err () => {
println ("Totally empty, init with __err__");
}
}
}
Results:
Totally empty, init with __err__
1.2. Void option type
Option type can store values of type void
, in that case the
Ok
pattern has no value to get. This can be usefull to execute a
function, and verify afterwards if it succeeded or not.
import std::io;
def foo (i : i32)-> void
throws &AssertError
{
assert (i < 10, "i must be lower than 10");
println (i);
}
def main () {
let x : void? = foo (10) ?
match x {
Ok () => {
println ("Foo succeeded");
}
Err (err : _) => {
println ("Foo failed, because : ", err);
}
}
}
Results:
Foo failed, because : core::exception::AssertError (i must be lower than 10)