I am just beginning Haskell, but from all the online tutorials I've found I can't seem to find if there is one accepted way to do a conditional control statement. I have seen if-else, guards, and pattern matching, but they all seem to accomplish the same thing. Is there one generally accepted/faster/more efficient way than the rest?
Well, I don't know if thinking in terms of "control statements" is the best way to go about it in Haskell. That said, it mostly all comes down to pattern matching in the end; boolean conditionals such as if ... then ... else
can be defined in terms of pattern matching on constructors for Bool
, for instance.
The most "primitive" form is probably the case
statement--pattern matching for function definitions is just syntactic sugar over a single function definition containing one big case
expression.
In terms of what you should use, go with whatever makes the most sense conceptually. Pattern matching is most appropriate for when you need to take apart an algebraic data type; if
blocks are appropriate for when you need a simple yes/no result for some predicate. Guards are generally used for when you need a mixture of data type deconstruction and boolean predicates.
The most significant point to keep in mind is that pattern matching is the only way to take apart an algebraic data type. Boolean predicates can be easily replaced with higher-order functions, but extracting values inside a data constructor requires pattern matching.
None of the three options do exactly the same thing or can be used in all situations.
Pattern matches check which constructor was used to create a given value and they bind variables. Neither if nor guards do that. You could only use them instead of pattern matches if you match against a nullary constructor (or a number literal) of a type implementing Eq.
Example:
foo (Just x) = x+1 -- Can not do this without a pattern match (except by using
-- functions like fromJust that themselves use pattern matches)
foo Nothing = 0 -- You could do this using a pattern guards like
-- foo x | x==Nothing = 0, but that is less readable and less
-- concise than using a plain pattern match
Pattern guards allow you to check for other things than equality. E.g. you can check whether a given number is greater than zero. Of course you can do the same thing with if, but pattern guards allow you to go to the next pattern when a guard fails, which can lead to less repetition than using if. Example:
maybeSqrt (Just x) | x >= 0 = sqrt x
maybeSqrt _ = Nothing
Using if this would look like this (note the repetition of Nothing):
maybeSqrt (Just x) = if x >= 0 then sqrt x
else Nothing
maybeSqrt _ = Nothing
Lastly if can be used without pattern matches. If you don't actually use pattern matching on a given value introducing a case x of ...
just so you can use pattern guards makes little sense and is less readable and concise than just using if.
I choose based on what makes the code look prettier and easier to read. As @Don points out, many of these different forms are compiled to case
. They look different because of the syntactic sugar that's available. This sugar isn't for the compiler, it's for humans. So decide based on what you think other humans would like to read, and what looks readable to you.