Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guards vs. if-then-else vs. cases in Haskell

People also ask

When should I use guards in Haskell?

Haskell guards are used to test the properties of an expression; it might look like an if-else statement from a beginner's view, but they function very differently. Haskell guards can be simpler and easier to read than pattern matching .

Does Haskell have if-else?

In the above example, we have seen the use of if-else statement in Haskell. Here, we will learn how to use multiple if-else statements in one Haskell program. In Haskell, multiple lines of if will be used by separating each of the if statement with its corresponding else statement.

Is there Elif in Haskell?

Note that Haskell does not have an "elif" statement like Python.

What is pattern matching Haskell?

Overview. We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.


From a technical standpoint, all three versions are equivalent.

That being said, my rule of thumb for styles is that if you can read it as if it was English (read | as "when", | otherwise as "otherwise" and = as "is" or "be"), you're probably doing something right.

if..then..else is for when you have one binary condition, or one single decision you need to make. Nested if..then..else-expressions are very uncommon in Haskell, and guards should almost always be used instead.

let absOfN =
  if n < 0 -- Single binary expression
  then -n
  else  n

Every if..then..else expression can be replaced by a guard if it is at the top level of a function, and this should generally be preferred, since you can add more cases more easily then:

abs n
  | n < 0     = -n
  | otherwise =  n

case..of is for when you have multiple code paths, and every code path is guided by the structure of a value, i.e. via pattern matching. You very seldom match on True and False.

case mapping of
  Constant v -> const v
  Function f -> map f

Guards complement case..of expressions, meaning that if you need to make complicated decisions depending on a value, first make decisions depending on the structure of your input, and then make decisions on the values in the structure.

handle  ExitSuccess = return ()
handle (ExitFailure code)
  | code < 0  = putStrLn . ("internal error " ++) . show . abs $ code
  | otherwise = putStrLn . ("user error " ++)     . show       $ code

BTW. As a style tip, always make a newline after a = or before a | if the stuff after the =/| is too long for one line, or uses more lines for some other reason:

-- NO!
nthElement (x:xs) a | a <= 0 = Nothing
                    | a == 1 = Just x
                    | a > 1 = nthElement xs (a-1)

-- Much more compact! Look at those spaces we didn't waste!
nthElement (x:xs) a
  | a <= 0    = Nothing
  | a == 1    = Just x
  | otherwise = nthElement xs (a-1)

I know this is question about style for explicitly recursive functions, but I would suggest that the best style is finding a way to reuse existing recursive functions instead.

nthElement xs n = guard (n > 0) >> listToMaybe (drop (n-1) xs)

This is just a matter of ordering but I think its very readable and has the same structure as guards.

nthElement :: [a] -> Int -> Maybe a 
nthElement [] a = Nothing
nthElement (x:xs) a = if a  < 1 then Nothing else
                      if a == 1 then Just x
                      else nthElement xs (a-1)

The last else doesn't need and if since there is no other possibilities, also functions should have "last resort case" in case you missed anything.