It would probably be clearer if R1
and R2
were named Ok1
and Ok2
.
There’s no “marshaling”. R2
is the Ok
type parameter of whatever action
Is provided. That’s how in one function it’s Text
and another it’s Bool
.
It would probably be clearer if R1
and R2
were named Ok1
and Ok2
.
There’s no “marshaling”. R2
is the Ok
type parameter of whatever action
Is provided. That’s how in one function it’s Text
and another it’s Bool
.
TIL Result.chain
in Motoko - thanks Paul!
Monads are a great abstraction for sequential computation with “short-circuiting”
You are probably aware of this, and explicitly avoiding it, but the code can be shortened, at the cost of increased nesting, by push the happy path continuations into the case branches, instead of returning to the outermost level:
let v = switch (exp) {
...
case (?x) { x }
};
decs
can be written as:
switch (exp) {
...
case (?v) { decs }
}
which avoids introducing and returning temporary x
.
In your example, a lot of the return
s can now also be elided since the function is about to exit anyway (after each case) so there is no need to return
early.
public shared(msg) func query_gender(user_id : Nat, access_id : Nat) : async Result.Result<Text, Text> {
switch(users.get(access_id)){
case(null) { #err("access user not found")};
case(?foundAccessUser) {
switch(users.get(user_id)){
case(null){ #err("user not found")};
case(?foundUser){
foundUser.access_log.add((foundAccessUser.id, Time.now()));
return #ok(
switch(foundUser.gender){
case(#M){"male"};
case(#F){"female"};
case(#O(val)){val};
})
}
}
}
}
};
Another trick is to exploit compound patterns, switching once on the result of several computations rather than switching separately on each, though that does involve doing the computations up front rather than on demand.
// Say the given phase.
public shared(msg) func is_female(user_id : Nat, access_id : Nat) : async Result.Result<Bool, Text> {
switch(users.get(access_id), users.get(user_id)){
case(null, _){ #err("access user not found") };
case(_, null){ #err("user not found") };
case(?foundAccessUser, ?foundUser) {
foundUser.access_log.add((foundAccessUser.id, Time.now()));
switch(foundUser.gender){
case(#O(val)){ #err("nyi for custom gender")};
case(#M){ #ok(false)};
case(#F){ #ok(true)};
};
}
};
};
Interestingly, to reduce the pain of match
(switch
),
Rust has if let pat = e {} <else {}>
conditional let statements:
(does it have conditional let expressions too?)
Similarly for while loops: while let pat = e {}
.
In case you previously participated and contributed:
let ?x = getMyNat() else {return #err("I just could not do it")};
let y = x + x;