How to have 2 different values in a case switch to do the same thing

G-d willing

Hello,
how can I have 2 different values in a case switch to do the same thing
For example,

data IntValues 
  = FirstInt
  | SecondInt
  | ThirdInt

returnIntValue : IntValues -> Int
returnIntValue val = case val of
  FirstInt -> 0
  SecondInt -> 1
  ThirdInt -> 1

How can I not duplicate the code when I am having SecondInt or ThirdInt. I tried using , and || but it didn’t work

The other option is to do it the following way:

returnIntValue val = case val of
  FirstInt -> 0
  val | val == SecondInt || val = ThirdInt -> 1

But this code is having an error Pattern match(es) are non-exhaustive.

You can use guards, either in the case expression or directly at the function level:

module Guards where

import DA.Assert
import Daml.Script

data IntValues 
  = FirstInt
  | SecondInt
  | ThirdInt
  | FourthInt
    deriving (Eq, Show)

returnIntValue : IntValues -> Int
returnIntValue val = case val of
  x | x == FirstInt || x == SecondInt -> 1
    | x == ThirdInt || x == FourthInt -> 2
    | otherwise -> error "impossible"

returnIntValue' : IntValues -> Int
returnIntValue' val
  | val == FirstInt || val == SecondInt = 1
  | val == ThirdInt || val == FourthInt = 2
  | otherwise = error "impossible"


test : Script ()
test = script do
  2 === returnIntValue ThirdInt
  2 === returnIntValue' ThirdInt
  return ()

They are mentioned only briefly in our docs here as an additional topic, but work exactly as in Haskell.

1 Like

G-d willing

Thanks @bernhard for your solution. Is there a way Daml can know that all pattern matches are exhaustive without using the otherwise ?

The Daml compiler only knows about exhaustiveness when you use pattern matches. When you use guards like above, the compiler is not smart enough to see that the expression is always true.

There are some ways to avoid guards here though: First you can use a wildcard match for the last two cases. That’s not a general solution but it works well if you just want to match on “everything else”.

returnIntValue : IntValues -> Int
returnIntValue val = case val of
  FirstInt -> 0
  _ -> 1

One downside of that is that you won’t get a compile error if you add a new case so it might silently do the wrong thing. You won’t get a compile error with guards either but at least you get a runtime error.

The other option is that you keep it as separate matches but you factor out the code and use it on the right hand side. In this example this looks a bit weird since you just have 1 there but if the code is more complex, this can often be a good solution. Note that the code you factor out could also be a function that takes arguments which is useful if you’re matching on constructors that take arguments.

returnIntValue : IntValues -> Int
returnIntValue val = case val of
    FirstInt -> 0
    SecondInt -> secondAndThird
    ThirdInt -> secondAndThird
  where secondAndThird = 1
2 Likes