Confusion about `instance Eq`

All of the examples I could find of instance Eq are derived. I don’t want to compare all of the fields in a record, just one. Given the code:

type Card = { id :: Int, color :: String, title :: String }

instance eqCard :: Eq Card where
  eq a b = a.id == b.id

I get the following error, which I don’t understand.

     v
  7  instance eqCard :: Eq Card where
  8    eq a b = a.id == b.id
                           ^

  Type class instance head is invalid due to use of type

    ( color :: String
    , id :: Int
    , title :: String
    )

  All types appearing in instance declarations must be of the form T a_1 .. a_n, where each type a_i is of the same form, unless the type is fully determined by other type class arguments via functional dependencies.

  in type class instance

    Data.Eq.Eq Card

Can somebody explain what is wrong? I’m using purs 0.15.6.

Thanks.

I’m not sure about this because the error message seems to indicate something different, but I think this is not compiling because you can’t add a typeclass instance to type Card because that’s an alias for a structural type.

You should create a newtype Card. Then Card is a new nominal type to which you can add instances.

Orphan Instances

newtype Card = Card { id :: Int, color :: String, title :: String }

instance eqCard :: Eq Card where
  eq (Card a) (Card b) = a.id == b.id
3 Likes

@jamesbrock is right about the problem and the solution; I just wanted to clarify what the error message is telling you.

What you’ve tried to specify is an instance of type Eq Card. As Card is a type synonym, this type expands to Eq { color :: String, id :: Int, title :: String }. The record type syntax in PureScript is actually just sugar for a primitive type called Record and a row type, so the previous desugars to:

Eq (Record (color :: String, id :: Int, title :: String))

The compiler is telling you that the row type part ((color :: String, id :: Int, title :: String)) is not allowed as part of the type of an instance declaration. All types involved in an instance declaration must be made up of type constructors (‘of the form T a_1 .. a_n,’ says the error message), and row types aren’t type constructors. (The error message then adds that functional dependencies can create a loophole for this rule, but that’s an advanced topic.)

7 Likes

I should add that having created a newtype, I need to pattern match using the constructor in the args. The implementation now becomes

instance eqCard :: Eq Card where
  eq (Card a) (Card b) = (a.id == b.id)
1 Like