In the following Motoko code, each action has the same error type “MyErr”. How do we run all actions and if any that fails returns its error?
func some(x:Action) : Result<(), MyErr> {
let #ok(resp) = action1(x) else return #err( _ ) // Won't work
switch(action1(x)) {
case (#ok()) ();
case (#err(e) return #err(e);
} // This works, but a lot of code is added, and having
// many actions it's not pretty anymore
switch(action2(x)) {
case (#ok()) ();
case (#err(e) return #err(e);
}
}
There is also this approach we use in Rechain
public func dispatch(action : A) : ({ #Ok : BlockId; #Err : E }) {
let reducerResponse = Array.map<ActionReducer<A, E>, ReducerResponse<E>>(reducers, func(fn) = fn(action));
let hasError = Array.find<ReducerResponse<E>>(reducerResponse, func(resp) = switch (resp) { case (#Err(_)) true; case (_) false });
switch (hasError) { case (? #Err(e)) { return #Err(e) }; case (_) () };
ignore Array.map<ReducerResponse<E>, ()>(reducerResponse, func(resp) { let #Ok(f) = resp else return (); f(blockId) });
Where each action returns Error or a function that should be executed if there are no errors at all.
action(x:Nat) : Result<() -> (), MyErr>
There are also these Result methods I haven’t seen examples of and may come in handy.
Perhaps this is the solution
func some(x:Action) : Result<(), MyErr> {
all_or_none([
func () = action1(x,y,z),
func () = action2(x),
//...
}
But would that cost too much instructions coming from the anonymous functions and the array?
Where all_or_none
runs all actions, checks if there are errors, and if not executes the functions. Or run_until_first_err
that returns the first error similar to the first example