Home > database >  Haskell types and typeclasses declaration
Haskell types and typeclasses declaration

Time:08-12

I am trying to understand classes and instances in Haskell a bit better. Therefore I am looking into:

http://learnyouahaskell.com/making-our-own-types-and-typeclasses

Here they recommend trying:

instance (Eq m) => Eq (Maybe m) where  
    Just x == Just y = x == y  
    Nothing == Nothing = True  
    _ == _ = False  

But when I do i get the following output:

tryouts.hs:58:10: error:
    Duplicate instance declarations:
      instance Eq m => Eq (Maybe m) -- Defined at tryouts.hs:58:10
      instance Eq a => Eq (Maybe a) -- Defined in `GHC.Maybe'
  1. How can I get past that and go instantiate my own typeclass-declarations?

  2. The author argues taht the (Eq m) => Eq in front of the class is important to restrain the content of the instance. This is apparantly different than if you do it at the class level (e.g.) when you define Eq. I am however not sure I understand what the difference is. THis might be where I don't understand the difference between a typeclass and a regular class in OOP and the nature of 'inheritance' for the former.

CodePudding user response:

  1. How can I get past that and go instantiate my own typeclass-declarations?

The error says that in the module, it already contains such instance. In fact it is semantically exactly the same one, as we can see in the source code:

data  Maybe a  =  Nothing | Just a
  deriving ( Eq  -- ^ @since 2.01
           , Ord -- ^ @since 2.01
           )

The default way to implement Eq is that it returns True if the data constructors of the two items are the same, and that the parameters are, element-wise, equal.

You can define your own types, and then define instances, like for example:

data Maybe2 a = Nothing2 | Just2 a

instance Eq m => Eq (Maybe2 m) where  
    Just2 x == Just2 y = x == y  
    Nothing 2== Nothing2 = True  
    _ == _ = False

The author argues that the (Eq m) => Eq in front of the class is important to restrain the content of the instance. This is apparantly different than if you do it at the class level (e.g.) when you define Eq. I am however not sure I understand what the difference is. THis might be where I don't understand the difference between a typeclass and a regular class in OOP and the nature of 'inheritance' for the former.

If you would use:

class Eq a => Eq (f a) where
    ...

Then it would mean that Eq (Maybe a) would imply Eq a, so it is the other way around. This would however never work, since if Eq (Maybe Int) would be an instance, then Eq Int is an instance, but Eq Int can only be an instance if somehow Int ~ f a (Int is the same type as f a), and a is an instance of Eq.

CodePudding user response:

  1. You can't really "redefine" Eq for Maybe a since it is already been defined in Prelude. That being said, there may be a way that you can redefine it by {-#LANGUAGE NoImplicitPrelude #-} and carefully import just enough types and type classes to avoid importing the file that contains the Eq definition for Maybe a, or perhaps you can use one of the pragmas to allow overlapping declarations, but I am not familiar with that so don't take my word on it.

  2. Eq m => Eq (Maybe m) means that in order for Maybe m to be an instance of the Eq type class, m must already be an instance of Eq. This is quite natural, since in order to compare Just a and Just b, you need to able to compare a and b. As a result, you cannot compare equality for things like Just ( ) and Just (-).

CodePudding user response:

Another approach you can use to try to play with imported by default from Prelude stuff - use hiding. In this case it can look like this:

import Prelude hiding (Maybe, Just, Nothing)

data  Maybe a  =  Nothing | Just a

instance (Eq m) => Eq (Maybe m) where  
    Just x == Just y = x == y  
    Nothing == Nothing = True  
    _ == _ = False  
  • Related