0
votes

What is the order in which Haskell guards are evaluated?

Say that I have a function which returns a Bool:

someFunc :: Bool -> Bool -> Bool
someFunc b1 b2
 | b1 == True && b2 == True = True
 | b1 == True = False
 | b1 == False .....
...

I think it was with Monads and the Do-notation that I read, that actions are sometimes not evaluated sequentially. That is if I have:

do { val1 <- action1
     val2 <- action2
     action3 }

It might be the case that val2 will be calculated before val1.

Is this the case for guards as well? Can they be evaluated out of order? If guards were sequential, then if the first statement evaluates to False, and the second evaluates to True, then I can conclude that b2 is False. Does this logic always hold?

Edit: By statements I mean guard 1 to 3

1
Guards are evaluated in order: if the first is true, then the function will take on its value. Otherwise, the subsequent guards are checked.Aplet123
You can pretend guards are evaluated in-order. Haskell does not have to actually do that, in theory, but the Haskell Report mandates that (simply put) a branch is taken if and only it is true and all the branches above are false. The simplest way to achieve this semantics is in-order evaluation, indeed.chi

1 Answers

4
votes

Evaluating the tests within guards can’t have any side-effects — unlike in procedural languages. So the order of evaluating the comparisons or Boolean connectives doesn’t make any difference to the semantics of the program.

Prioritising the branches — that is, each of the lines starting | — is from top to bottom. But really ‘evaluating’ is the wrong concept: it would be OK for the compiler to first evaluate your b1 == False, providing it didn’t take the third branch until it had checked the first two. (GHC doesn’t actually do that; I’m just setting up a straw man.)

Note that in a call to someFunc, the arguments for b1, b2 might be arbitrarily complex expressions. Haskell’s ‘Lazy semantics’ mean that neither of them are evaluated until needed.

Does this logic always hold?

Be careful: if an early guard turns out False, you can’t assume anything about the expressions in it. The compiler might have rearranged them for efficiency, evaluated one out of textual order, then moved on. In your example, if for the first branch it turned out b1 /= True, the compiler might not evaluate b2 at all. So you can’t conclude anything about b2. Indeed b2 might give bottom/infinite calculation, if evaluated.

It’s not just with Monads or Do-notation (which are the same thing) that expressions are not necessarily evaluated in textual order — that’s true across any expressions in any context. (The IO Monad has some dodgy semantics, to make it seem ‘statements’ are executed top-to-bottom.)