package core
+//
+// Result is helper type that encapsulates the idea of a computation
+// that could either succeed with a value, or fail with an error.
+// Generally, this is only used as the return type of a procedure,
+// but it can be used elsewhere. Like Optional, there are several
+// helper methods that make it easier to work with Results.
+//
+
+
use core {Optional, conv}
+#doc """
+ Result(T, E) is a structure that represents either an Ok value
+ of type T, or an Err value of type E. `status` contains either
+ .Ok, or .Err depending on which is currently held.
+"""
@conv.Custom_Format.{ #solidify format {T=Ok_Type, E=Err_Type} }
Result :: struct (Ok_Type: type_expr, Err_Type: type_expr) {
status: Result_Status;
__data: Result_Data(Ok_Type, Err_Type);
}
+#doc """
+ The type of Result(T, E).status
+"""
Result_Status :: enum {
Ok :: 1;
Err :: 2;
#inject Result {
+ #doc "Quick way to return an Ok from a procedure."
return_ok :: macro (x: $T) do return .{ .Ok, .{ value = x } };
+
+ #doc "Quick way to return an Err from a procedure."
return_err :: macro (x: $T) do return .{ .Err, .{ error = x } };
+ #doc "Returns true if the result contains an Ok value."
is_ok :: (r: #Self) => r.status == .Ok;
+ #doc "Returns true if the result contains an Err value."
is_err :: (r: #Self) => !r.status == .Err;
+ #doc "Returns an Optional of the Ok type."
ok :: (r: #Self) -> Optional(r.Ok_Type) {
if r.status == .Ok do return Optional.make(r.__data.value);
return .{};
}
+ #doc "Returns an Optional of the Err type."
err :: (r: #Self) -> Optional(r.Err_Type) {
if r.status == .Err do return Optional.make(r.__data.error);
return .{};
}
+ #doc """
+ Forcefully extracts the Ok value out of the Result. If the
+ result contains an Err, an assertion is thrown.
+ """
unwrap :: (r: #Self) -> r.Ok_Type {
if r.status == .Err {
msg := tprintf("Unwrapping Result with error '{}'.", r.__data.error);
return r.__data.value;
}
+ #doc """
+ Tries to extract the Ok value out of the Result. If the
+ result contains an Err, the empty .{} value is returned.
+ """
unwrap_or_default :: (r: #Self) -> r.Ok_Type {
if r.status == .Err do return .{};
return r.__data.value;
}
+ #doc """
+ Tries to extract the Ok value out of the Result. If the
+ result contains an Err, a custom assertion message is thrown.
+ """
expect :: (r: #Self, msg: str) -> r.Ok_Type {
if r.status == .Err {
assert(false, msg);
return r.__data.value;
}
- transform :: (r: #Self, f: (r.Ok_Type) -> $R) => {
- if r.status == .Err do return r;
+ #doc """
+ Returns a new result defined by:
+ Ok(n) => Ok(f(n))
+ Err(e) => Err(e)
+ """
+ transform :: (r: Result($T, $E), f: (T) -> $R) -> Result(R, E) {
+ if r.status == .Err do return .{ .Err, .{ error = r.error } };
return .{ .Ok, .{ value = f(r.__data.value) } };
}
+ #doc "Monadic chaining operation."
and_then :: (r: #Self, f: (r.Ok_Type) -> Result($R, r.Err_Type)) -> Result(R, r.Err_Type) {
if r.status == .Err do return r;
return .{ .Ok, .{ value = f(r.__data.value) } };
}
- or_else :: (r: Result, generate: () -> typeof r) => {
+ #doc "If the Result contains Err, generate is called to make a value"
+ or_else :: (r: #Self, generate: () -> typeof r) => {
if r.status == .Ok do return r;
return .{};
}
+ #doc """
+ If result contains Err, the error is returned from the enclosing
+ procedure. Otherwise, the Ok value is returned.
+
+ f :: () -> Result(i32, str) {
+ Result.return_err("Oh no...");
+ }
+
+ g :: () -> Result(str, str) {
+ // This returns from g with the error returned from f.
+ v := f()->forward_err();
+ println(v);
+
+ Result.return_ok("Success!");
+ }
+ """
forward_err :: macro (r: Result($T, $E)) -> T {
res := r;
if res.status == .Ok do return res.__data.value;
return #from_enclosing .{ .Err, .{ error = res.__data.error } };
}
+ #doc """
+ If result contains Err, the given value is returned from the
+ enclosing procedure. Otherwise, the Ok value is returned.
+ """
or_return :: macro (r: Result($T, $E), v: $V) -> T {
res := r;
if res.status == .Ok do return res.__data.value;
return #from_enclosing v;
}
+ #doc """
+ If result contains Err, the given code is run. This code is
+ expected to either:
+ - Return a good value with `return`
+ - Return an error value with `return #enclosing_scope`
+
+ This procedure is subject to change.
+ """
catch :: macro (r: Result($T, $E), on_err: Code) -> T {
res := r;
if res.status == .Ok do return res.__data.value;