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'
How can I get past that and go instantiate my own typeclass-declarations?
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 defineEq
. 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:
- 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:
You can't really "redefine"
Eq
forMaybe 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 theEq
definition forMaybe 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.Eq m => Eq (Maybe m)
means that in order forMaybe m
to be an instance of theEq
type class,m
must already be an instance ofEq
. This is quite natural, since in order to compareJust a
andJust b
, you need to able to comparea
andb
. As a result, you cannot compare equality for things likeJust ( )
andJust (-)
.
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