In C3 optionals are built into the language. They're not the run of the mill optionals as they carry a "optional result value". This makes them more like "Result" types than optionals.
In C3 you declare a variable holding an optional using the !
suffix:
int! x = ...
We can now assign either to the real value, or to the optional result:
int! x = 1; // x is a real value x = MyRes.MISSING!; // x is assigned an optional // x = MyRes.MISSING; <- Error: cannot assign "MyRes" to int
If we think of it in terms of a "Result":
Result<int> x; x.result = 1; // x = 1 x.error = MyRes.ERR; // x = MyRes.ERR! x.result = MyRes.ERR; // x = MyRes.ERR - fails
So the "clever" !
suffix here is used to assign to the "error" part of the Result. Unfortunately, the suffix is hard to read at the end of a line, where !
and ;
often blurs together. For that reason I regularly try to revisit this syntax to see if I can improve on it.
It's used in two cases:
- assignment:
x = MyRes.ERR!
- return:
return MyRes.ERR!
While (2) could be replaced by something like return! MyRes.ERR
or even raise MyRes.ERR
, the assignment is not as easily tackled.
Naive ideas could be to use some symbol salad like:
int! x !!= MyRes.MISSING; int! x <!= MyRes.MISSING; int! x <- MyRes.MISSING; // I'm going to exclude // int! x := MyRes.MISSING // as it is used as regular assign in most languages.
Or we could allow those return statements to have a different meaning in an assignment:
int! x = raise MyRes.MISSING; int! x = return! MyRes.MISSING;
While it's possible, it creates an odd effect if we consider this example:
int! x; return x = return! MyRes.MISSING;
This should also illustrate that using x = MyRes.MISSING!
should be thought of as implicitly doing x = { 0, MyRes.MISSING }
.
Understanding that we see how it works:
x = MyRes.MISSING!; // x = { 0, MyRes.MISSING } return MyRes.MISSING!; // return { 0, MyRes.MISSING }
So really the proper way would be to always translate the !
, like this:
x = fault MyRes.ERR; return fault MyRes.ERR;
Which is a mouthful. One could of course contract that return fault
into something like:
x = fault MyRes.ERR; throw MyRes.ERR;
Unfortunately, this builds the assumption that a return
may not return an optional, which it of course can:
int! x = ... return x; // Optional, so it may be like a "throw" or not
If we want to be super clear we can do something like this:
int! x = ... if (y) return? x; // Might return an optional if (z) return! MyRes.MISSING; // Will return an optional. if (w) return w; // Will not return an optional
Due to the ?
being a rethrow, we could require this:
int! x = ... if (y) return x?; // Use rethrow to make the type int if (z) return! MyRes.MISSING; // Will return an optional. if (w) return w; // Will not return an optional
So the question here is if this adds anything over the original:
int! x = ... if (y) return x; if (z) return MyRes.MISSING!; if (w) return w;
These are questions that need quite a bit of C3 error handling code to decide, so for now things have to stay as they are.