How to ignore diverging types in an if/else expression?
The following piece of code fails to typecheck with the following message:
• Couldn't match type ‘B’ with ‘A’
Expected type: Update (ContractId A)
Actual type: Update (ContractId B)
• In the expression: create (B s)
In a stmt of a 'do' block:
_ <- if flag then create (A s) else create (B s)
In the expression:
do if flag then create (A s) else create (B s)
return ()typecheck
template A
with
s: Party
where
signatory s
template B
with
s: Party
where
signatory s
template Helper
with
s: Party
where
signatory s
nonconsuming choice DoIt: ()
with
flag: Bool
controller s
do
if flag then
create (A s)
else
create (B s)
return ()
I do not care about return values of neither create A and create B.
What’d be a clean way to make this code compile?
My current workaround is:
template Helper
with
s: Party
where
signatory s
nonconsuming choice DoIt: ()
with
flag: Bool
controller s
do
if flag then
(create (A s), return ())._2
else
(create (B s), return ())._2
return ()
I’ve found a nicer way:
template Helper
with
s: Party
where
signatory s
nonconsuming choice DoIt: ()
with
flag: Bool
controller s
do
if flag then
do
create (A s)
return ()
else
do
create (B s)
return ()
The most efficient way and also the simplest is something like this
if flag
then
fmap (\_ -> ()) (create (A s))
else
fmap (\_ -> ()) (create (B s))
We can then simplify that in two steps: First for the special case of fmap (\_ -> a) we can use a <$
if flag
then
() <$ create (A s)
else
() <$ create (B s)
And then for the special case of () <$ there is a functoin called void:
if flag
then
void $ create (A s)
else
void $ create (B s)
Hi @pbatko,
I want to second @cocreature’s approach of using void here to discard the return value of an action:
if flag then
void $ create (A s)
else
void $ create (B s)
But I also want to point out that the first workaround you suggested:
if flag then
(create (A s), return ())._2
else
(create (B s), return ())._2
Does not work as intended. Those create actions are never executed, only the return actions.
In Daml, if you have an expression of the form (a, b)._2 then both a and b will be evaluated as expressions, but only b will be executed as an action. This is a consequence of Daml being a purely functional programming language, like Haskell, where simply evaluating an expression does not cause any side effect (like the effect of “create”) to happen.
To actually execute a sequence of actions in Daml, the actions have to be linked together using do notation. So the right way to evaluate and execute two actions, while ignoring the result of the first, is by using do notation, like in your second suggestion:
do
a
b
Alternatively, you can use the >> operator, which uses the same mechanism as do notation to link two actions together:
a >> b