[Experimental] Add 'try' and `or_else' built-in procedures

This commit is contained in:
gingerBill
2021-07-04 01:38:43 +01:00
parent 1c76577918
commit e8f2c5a48a
6 changed files with 628 additions and 50 deletions
+141
View File
@@ -1999,6 +1999,146 @@ relative_data_types :: proc() {
fmt.println(rel_slice[1]);
}
try_and_or_else :: proc() {
fmt.println("\n#try(...) and or_else(...)");
// IMPORTANT NOTE: 'try' and 'or_else' are experimental features and subject to change/removal
Foo :: struct {};
Error :: enum {
None,
Something,
Whatever,
};
bar :: proc(ok: bool) -> (f: Foo, err: Error) {
if !ok {
err = .Something;
}
return;
}
try_return_value :: proc() -> Error {
// This is a common idiom, where the end value of an expression
// may not be 'nil' or may be 'false'
f0, err := bar(true);
if err != nil {
return err;
}
_ = f0;
// 'try' is a lovely shorthand that does this check automatically
// and returns early if necessary
f1 := try(bar(true));
fmt.println(#procedure);
fmt.println(f1);
f2 := try(bar(false));
fmt.println(#procedure);
fmt.println(f2);
return .None;
}
try_return_value2 :: proc() -> (i: int, err: Error) {
// 'try' will work within procedures with multiple return values
// However, the return values must be named
// 'try' effectively pops off the last value and checks it
// And then returns the rest of the values, meaning it works
// for as many return values as possible
i = 0;
f0, f0_err := bar(true);
if f0_err != nil {
err = f0_err;
return;
}
fmt.println(#procedure);
fmt.println(f0);
// The above can be translated into 'try'
i = 1;
f1 := try(bar(true));
fmt.println(#procedure);
fmt.println(f1);
i = 2;
f2 := try(bar(false));
fmt.println(#procedure);
fmt.println(f2);
i = 3;
return i, .None;
}
try_return_value4 :: proc() -> (i: int, j: f64, k: bool, err: Error) {
f := try(bar(false));
fmt.println(#procedure);
fmt.println(f);
return 123, 456, true, .None;
}
try_optional_ok :: proc() -> bool {
m: map[string]int;
/*
f1, ok := m["hellope"];
if !ok {
return false;
}
*/
// 'try' equivalent
f2 := try(m["hellope"]);
fmt.println(f2);
return true;
}
{
// 'try' examples
err := try_return_value();
fmt.println(err);
ok := try_optional_ok();
fmt.println(ok);
i, err2 := try_return_value2();
fmt.println(i);
fmt.println(err2);
a, b, c, err4 := try_return_value4();
assert(a == 0 && b == 0 && c == false && err4 == .Something);
}
{
// 'or_else' does a similar value check as 'try' but instead of doing an
// early return, it will give a default value to be used instead
m: map[string]int;
i: int;
ok: bool;
if i, ok = m["hellope"]; !ok {
i = 123;
}
// The above can be mapped to 'or_else'
i = or_else(m["hellope"], 123);
assert(i == 123);
}
{
// 'or_else' can be used with type assertions too, as they
// have optional ok semantics
v: union{int, f64};
i: int;
i = or_else(v.(int), 123);
i = or_else(v.?, 123); // Type inference magic
assert(i == 123);
m: Maybe(int);
i = or_else(m.?, 456);
assert(i == 456);
}
}
main :: proc() {
when true {
the_basics();
@@ -2031,5 +2171,6 @@ main :: proc() {
union_maybe();
explicit_context_definition();
relative_data_types();
try_and_or_else();
}
}