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) ?
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.