Ando Bando
Ando Bando

Reputation: 113

Pattern Matching for Alternate instances of Eq

So, I create a data type

data Expr a = Const a
    | Expr a :*: Expr a

And I define an instance for Eq for this data type

instance (Eq m) => Eq (Expr m) where
    Const a == Const b = a == b
    (a :*: b) == (c :*: d) = ((a == c) && (b == d)) || ((a == d) && (b == c))

This definition means that order does not matter when comparing two expressions. However, when I use pattern matching to write a function that takes Exprs I cannot just write

f (Const 1 :*: a) = ...

but must also write

f (a :*: Const 1) = ...

in order to catch all cases, even though if I where to compare the two using (==) it would return true.

Is there a way to write just one of the above to expressions and let the instance of Eq take care of the rest? Does pattern matching even use or require an instance of Eq?

Upvotes: 1

Views: 204

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170745

Is there a way to write just one of the above to expressions and let the instance of (Eq) take care of the rest?

No.

Does pattern matching even use or require an instance of (Eq)

Only when matching against (number, character or string) literals. You can see the rules in 3.17.2 of https://www.haskell.org/onlinereport/exps.html#pattern-matching and note that case 7 is the only one which mentions ==.

But you can use view patterns:

const1 (Const 1 :*: a) = Just a
const1 (a :*: Const 1) = Just a
const1 _ = Nothing

f (const1 -> Just a) = ...

This may be too ugly if you need other symmetries in other cases and I can't think of a solution for this at the moment.

Upvotes: 3

leftaroundabout
leftaroundabout

Reputation: 120711

An Eq instance has no bearing whatsoever on pattern matches. == is just a library function and checks a particular property that doesn't necessarily have much to do with implementation structure, whereas pattern matches are all about deconstructing a type in its actual ADT structure.

That said, it is possible to fake something similar to what you asked, but I daresay it's not a very good idea to actually do this – doesn't scale and might lead to strange issues later on.

{-# LANGUAGE PatternSynonyms, ViewPatterns #-}

matchConstFactor :: Expr a -> Maybe (a, Expr a)
matchConstFactor (Const a :*: b) = Just (a, b)
matchConstFactor (a :*: Const b) = Just (b, a)
matchConstFact _ = Nothing

pattern (:.*:) :: a -> Expr a -> Expr a
pattern a :.*: b <- (matchConstFactor -> Just (a, b))
 where a :.*: b = Const a :*: b

Upvotes: 3

Related Questions