If-then-else without else

Hi! I wrote a code like this:

nonconsuming choice SomeChoice : ContractId SomeContract
      with
        someField1 : Common.Id
        someField2 : Decimal
        someField3 : Party
      controller someController
      do
        assertMsg "Some error text" $ someCondition
        template1Cid <- functionOnTemplate1Cid temp1Cid party1 party2
 
        if someCondition2 then do
          (template1Cid, _) <- exercise template1Cid SomeChoiceOnThatTemplate with someParam = someValue

        template123Cid <- exercise template1Cid SomeChoice2OnThatTemplate with someParam2 = someValue2
        archive self
        create ThirdTemplate with ..

I only doubting about if-then statement without else part. Is it legal to write so if I just aiming to include 1 conditional action? Any meaningful pointers to the Daml reference are highly appreciated!

Hi Oleg,

You should use If ... Else the same as many other programming languages.

Please refer to the If-then-Else documentation for some straightforward examples, and also read the Functional Programming 101 If…Else control flow examples.

Regards, Ben.

Hi @Oleg,

Daml is a purely functional language, which among other things means that every code construct is an expression, and every expression returns a value. It’s also a statically typed language, meaning every expression must have a (statically defined) type.

So the if condition then then-expression else else-expression construct is an expression that returns a value of a given type. From a type perspective:

  • The condition expression needs to evaluate to a Boolean value.
  • The then-expression and the else-expression must have the same type, which will also be the type of the overall if expression.

So in a = if ... then ... else ..., a can have a single, defined type.

The language does not support an if expression without an else branch. Given the above constraints, how could it? If you write

a = if False then 4

what is the type of a? What is the value of a? That would just not make sense.

Now, in the specific context of a do block, it’s tempting to think of if as a statement rather than an expression. But the language does not make a special case here; it’s the same if construct and it’s just as much of an expression. Within a do block, a line of the form

if ... then ... else ...

is a shorthand for

_unused_name <- if ... then ... else ...

which is to say we are still expecting a resulting value and we are still capturing it in a local binding, even though that binding is not getting used later on. Thus we still need an else branch to get a meaningful, well-typed expression.

If you’re not meaning to use the result of your then branch, you can rewrite your code to

        if someCondition2 then do
          exercise template1Cid SomeChoiceOnThatTemplate with someParam = someValue
          pure ()
        else
          pure ()

such that both branches return an Action ().

1 Like

Hi @Gary_Verhaegen ,
Thanks for your reply.
Let me try answer this one:
If you write

a = if False then 4

what is the type of a? What is the value of a? That would just not make sense.

The type might be Optional Int and value None, Well, with a small adjustment “Some 4” instead of just 4. And assuming If could infer the type of missed else part to be the same as for then one. Which I believe not a big deal for Daml. It guesses sometimes much less obvious things.
But here comes the reason why I cleaned up my code to paste it here, for you guys to find out the specific usage context of a do block, NOT the variable value assignment.
Transferring your example closer to mine, type of my incomplete if…else might be Optional Action() or so.
However your answer inspired me to check the reference for Action () type (hope it’s type and I’m not mixing up things) description, and I surprisingly found even more elegant solution - when function:

when final (archive contractId)

I used it in my code and … no squiggles!

when someCondition2 ((template1Cid, _) <- exercise template1Cid SomeChoiceOnThatTemplate with someParam = someValue)

Looks nice and concise. If it really works as I expected then I guess it’s a perfect solution.

If I’m wrong, please let me know.

Few more reasons why I asked it here:
-no errors were given by Daml extension (VS Code) to my code, well at least no squiggles (didn’t try to build yet)
-no clear syntax reference which will give an idea of what is required and what is optional part of construction. Only examples.
-similar stackoverflow Haskell (which as I know the nearest relative for Daml) discussion. No clear (at least for me) answer there.

So then the issue you’d get here is that your if appears in a position where Daml requires an Action _ type, and if it gets an Optional (Action _) it won’t be able to proceed. You’d need to unwrap the Optional first. We could, in general, say that if an if expression doesn’t have an else, we implicitly wrap it into an Optional, but then in your case you’d have to write something like:

nonconsuming choice SomeChoice : ContractId SomeContract
      with
        someField1 : Common.Id
        someField2 : Decimal
        someField3 : Party
      controller someController
      do
        assertMsg "Some error text" $ someCondition
        template1Cid <- functionOnTemplate1Cid temp1Cid party1 party2
 
        let opt = if someCondition2 then do
          (template1Cid, _) <- exercise template1Cid SomeChoiceOnThatTemplate with someParam = someValue
        case opt of
          Nothing -> pure ()
          Some action -> action

        template123Cid <- exercise template1Cid SomeChoice2OnThatTemplate with someParam2 = someValue2
        archive self
        create ThirdTemplate with ..

and I don’t think you’d gain much there.

Using when is a good option here, but I feel compelled to point out that when is not magic, is not special, and is not a built-in (unlike if). Specifically, it is defined here as:

when : Applicative f => Bool -> f () -> f ()
when p s  = if p then s else pure ()
1 Like