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)
1 Like

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
3 Likes