The variables used in pattern matching are considered local variables. Consider this definition to calculate the length of a list:
len (x:xs) = 1 + len xs len _ = 0
The variables x and xs are local variables for 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 value for len . In more detail, the first pattern (x:xs) not equivalent (10:xs) . If this were interpreted in this way, we would now have len [5,6] == 0 , violating the previous code! Fortunately, the pattern matching semantics are resistant to new ads like x=10 .
Your code
squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a squashMul xy | (Lit mulId) <- x = y | (Lit mulId) <- y = x squashMul xy = Mul xy
really mean
squashMul :: (Ring a) => RingExpr a -> RingExpr a -> RingExpr a squashMul xy | (Lit w) <- x = y | (Lit w) <- y = x squashMul xy = Mul xy
which is wrong, since w can be arbitrary. You probably want:
squashMul :: (Eq a, Ring a) => RingExpr a -> RingExpr a -> RingExpr a squashMul xy | (Lit w) <- x , w == mulId = y | (Lit w) <- y , w == mulId = x squashMul xy = Mul xy
(The Eq a constraint may depend on a RingExpr definition that has not been published)
You can also simplify everything:
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 xy = Mul xy
or even:
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 xy = Mul xy
In this version, template protections are not even used, since there is no need.