Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not sure why this pattern guard matches

Learning Haskell and I am not sure why I don't get the expected result, given these definitions:

instance Ring Integer where
  addId  = 0
  addInv = negate
  mulId  = 1

  add = (+)
  mul = (*)

class Ring a where
  addId  :: a            -- additive identity
  addInv :: a -> a       -- additive inverse
  mulId  :: a            -- multiplicative identity

  add :: a -> a -> a     -- addition
  mul :: a -> a -> a     -- multiplication

I wrote this function

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit mulId) <- x = y
  | (Lit mulId) <- y = x
squashMul x y = Mul x y

However:

*HW05> squashMul (Lit 5) (Lit 1)
Lit 1

If I write one version specifically for Integer:

squashMulInt :: RingExpr Integer -> RingExpr Integer -> RingExpr Integer
squashMulInt x y
  | (Lit 1) <- x = y
  | (Lit 1) <- y = x
squashMulInt x y = Mul x y

Then I get the expected result.

Why does (Lit mulId) <- x match even when x is not (Lit 1) ?

like image 486
j-a Avatar asked Dec 16 '14 18:12

j-a


People also ask

Can you pattern match in guards Haskell?

The PatternGuards extension, now officially incorporated into the Haskell 2010 language, expands guards to allow arbitrary pattern matching and condition chaining. The existing syntax for guards then becomes a special case of the new, much more general form. You start a guard in the same way as always, with a | .

How do I use pattern matching in Haskell?

You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again.


1 Answers

Variables used in pattern matching are considered to be local variables. Consider this definition for computing the length of a list:

len (x:xs) = 1 + len xs
len _      = 0

Variables x and xs are local variables to this definition. In particular, if we add a definition for a top-level variable, as in

x = 10
len (x:xs) = 1 + len xs
len _      = 0

this does not affect the meaning for len. More in detail, the first pattern (x:xs) is not equivalent to (10:xs). If it were interpreted in that way, we would now have len [5,6] == 0, breaking the previous code! Fortunately, the semantics of pattern matching is robust to such new declarations as x=10.

Your code

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit mulId) <- x = y
  | (Lit mulId) <- y = x
squashMul x y = Mul x y

actually means

squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit w) <- x = y
  | (Lit w) <- y = x
squashMul x y = Mul x y

which is wrong, since w can be arbitrary. What you want is probably:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x y
  | (Lit w) <- x , w == mulId = y
  | (Lit w) <- y , w == mulId = x
squashMul x y = Mul x y

(The Eq a constraint may depend on the definition of RingExpr, which was not posted)

You can also simplify everything to:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul x@(Lit w) y         | w == mulId = y
squashMul x         y@(Lit w) | w == mulId = x
squashMul x         y                      = Mul x y

or even to:

squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a
squashMul (Lit w) y       | w == mulId = y
squashMul x       (Lit w) | w == mulId = x
squashMul x       y                    = Mul x y

This version does not even use pattern guards, since there's no need to.

like image 110
chi Avatar answered Sep 17 '22 21:09

chi