views:

297

answers:

4

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?

+4  A: 

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.

camccann
+4  A: 

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.

sepp2k
+2  A: 

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.

+8  A: 
Norman Ramsey
(Of course, you should use `null xs` to check if `xs` is empty.)
KennyTM
@Kenny If I had a dollar for every student of mine who wrote `length xs == 0` instead of `null xs`, I'd have enough to keep me in coffee for the year :-)
Norman Ramsey
Thank you, this was very helpful. I'm just going to use pattern matching until I get more experience.EDIT: +1 because after I posted this I viewed your profile and saw you were a professor at Tufts. I'm going there next year as a CS major.
Nathan