简体   繁体   中英

In Haskell, are guards or matchers preferable?

I'm learning Haskell, and it's not always clear to me when to use a matcher and when to use a guard. For certain scenarios it seems that matchers and guards can be used to achieve essentially the same ends. Are there some rules or heuristics for when it's better to use matches over guards or vice versa? Is one more performant than the other?

To illustrate what I'm getting at, here are a couple of silly examples I cooked up that seem to be equivalent, but one version uses matchers and the other uses guards:

listcheck :: [a] -> String
listcheck [] = "List is null :-("
listcheck a = "List is NOT null!!"

listcheck' a
    | null a = "List is null :-("
    | otherwise = "List is NOT null!!"

and

luckyseven :: Int -> String
luckyseven 7 = "SO LUCKY!"
luckyseven b = "Not so lucky :-/"

luckyseven' c
    | c == 7 = "SO LUCKY!"
luckyseven' c = "Not so lucky :-/"

Thanks!

These can often be used interchangeably, but there are significant differences between the two. Pattern matching can only occur on constructors, so computations can not be performed inside of a pattern, while guards are simply multi-branch if-else statements. For example, I can't write a pattern equivalent of the following:

func :: Int -> Int
func x
    | even x = 3 * x
    | odd x  = 7 * x        -- alternatively "otherwise = 7 * x" to get rid of all those pesky compiler warnings

This just wouldn't be possible with just pattern matching. You also can't do things like

func :: Int -> Maybe String
func x
    | x < 0     = Nothing
    | x == 0    = Just "Zero"
    | x < 20    = Just "Small"
    | x < 100   = Just "Big"
    | x < 1000  = Just "Huge"
    | otherwise = Just "How did you count that high?"

Conversely, guards using ADTs don't give you much information without helper functions. If I had the type

data Expr
    = Literal Int
    | Add  Expr Expr
    | Mult Expr Expr
    | Negate Expr
    deriving (Eq, Show)

Using guards to write the equivalent of

eval :: Expr -> Int
eval (Literal i)  = i
eval (Add  e1 e2) = eval e1 + eval e2
eval (Mult e1 e2) = eval e1 * eval e2
eval (Negate e)   = negate (eval e)

would be a lot more verbose, difficult, and annoying. In fact, at some level you'd have to resort to pattern matching to do things like

getLiteral :: Expr -> Int
getLiteral (Literal i) = i
getLiteral _           = error "Not a literal"

Which introduces functions that can error , which is bad. In this case, using pattern matching is much preferred over using guards.

For your particular examples, I'd go with pattern matching, but would use _ where possible:

listCheck :: [a] -> String
listCheck [] = "List is null :-("
listCheck _  = "List is NOT null!!"

and

luckySeven :: Int -> String
luckySeven 7 = "SO LUCKY!"
luckySeven _ = "Not so lucky :-/"

That emphasizes that if the list isn't empty, or the Int isn't 7, nothing else matters, and you aren't going to use its particular value to produce the function result. bheklilr has capably pointed out places where one choice or the other is definitely preferable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM