stcol
stcol

Reputation: 141

How do I correctly implement an instance Eq for a data type with constructors?

I am implementing an instance Eq for MyNum data type that has Fractions and Mixed Numbers as data constructors (each one with its own arguments).

I am also using a helper function to convert from MyNum to Rational. This should help me reduce fractions when doing comparisons in the instance Eq.

The problem is I am getting stuck when creating the instance Eq and I am not sure whether I also have to define a new class Eq for the MyNum data type. The general purpose of the Eq is to compare whether two fractions are equal when simplified with the helper method. Here is what I have so far:

Data Type and Constructors:

data MyNum = Fraction {num :: Integer, denom :: Integer} 
             | Mixed {whole :: Integer, num:: Integer, denom :: Integer}

Helper:

helper :: MyNum -> Rational
helper (Fraction num denom) = (fromIntegral num ) / (fromIntegral denom)

Current Eq:

instance (Eq n d) => Eq (MyNum n d) where
    Fraction n d == Fraction n d = True
--  Mixed _ _ _ == Mixed _ _ _ = True

The above Eq code throws the following error:

MyNum.hs:29:16: error:
    * Conflicting definitions for `d'
      Bound at: MyNum.hs:29:16
                MyNum.hs:29:32
    * In an equation for `=='
   |
29 |     Fraction n d == Fraction n d = True
   |                ^^^^^^^^^^^^^^^^^

Upvotes: 1

Views: 1034

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

The reason this happens is because you use n and d twice in the head of your == method definition, which is not allowed.

Both Fraction n d and Fraction n d in Fraction n d == Fraction n d = True are patterns, and using the same variable in more than one pattern on the left of the = sign in not allowed.

You would also need to write the type constraints as (Eq n, Eq d) =>…, and not Eq n d, but here it makes no difference since you do not use type parameters anyway, so no Eq constraint is needed at all -- the type of the values used by your MyNum type, which are all Integers, already is an instance of Eq:

instance Eq MyNum where
    Fraction n1 d1 == Fraction n2 d2 | n1 == n2 && d1 == d2  =  True
    -- …

Prolog, which works with unification indeed allows us to use the same variable multiple times in the head, to indicate equality.

But Haskell works with pattern matching, and it is therefore not entirely clear what reusing the same variable would mean, especially since not every type is an instance of Eq.

Upvotes: 4

chi
chi

Reputation: 116139

You might want to exploit your helper function to convert both MyNums into Rationals so that you can compare them:

instance Eq MyNum where
   x == y  =  helper x == helper y

Your type MyNum does not take any parameters, so you can't write Eq (MyNum n d). The n and d are values, arguments for the Fraction data constructor, not the type. Since these values are handled by the helper, we don't need to care about them in the instance definition either.

Note that, for the above instance to work, helper must deal with both forms of number, Fraction and Mixed.

helper:: MyNum -> Rational
helper (Fraction num denom)    = fromIntegral num / fromIntegral denom
helper (Mixed whole num denom) = ...

Upvotes: 3

Related Questions