I'm trying to write an Eq
instance for an EitherT
newtype given by:
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
I assumed the following Eq
instance would work:
instance (Eq e, Eq a, Eq m) => Eq (EitherT e m a) where
a == b = (runEitherT a) == (runEitherT b)
However, I'm seeing an error:
Expected kind '* -> *', but 'm' has kind '*'
What I'm reading from that error is that my typeclass constraint ( ... Eq m) => ...
is confusing the compiler into thinking that I believe m
to be of kind *
, when my newtype declaration for EitherT
expects it to be of kind * -> *
.
I'm wondering what I need to do, to declare that I want an Eq
instance for some higher kinded type m
to implement Eq
for my EitherT
newtype.
Edit: As pointed out by @AlexisKing, I can get this to work with:
{-# LANGUAGE UndecideableInstances #-}
instance (Eq (m (Either e a))) => Eq (EitherT e m a) where
a == b = (runEitherT a) == (runEitherT b)
However, it seems strange to me to that a language extension is required to write this Eq
instance. Is there no other way to express such a typeclass constraint in vanilla Haskell? If not, why?
Eq (m b)
, notEq m
. For what it’s worth, though, I think you could just have GHC derive this class for you, and it would figure out the necessary constraints. – Alexis KingVariable 'b' occurs more often in the constraint 'Eq (m b)' than in the instance head
. Also, ghc will not derive this typeclass for me, I'm not entirely sure why though. – WilduckStandaloneDeriving
andUndecidableInstances
and writederiving instance Eq (m (Either e a)) => Eq (EitherT e m a)
. Alternatively, you can write the instance you currently have, but replace all your constraints withEq (m (Either e a))
. – Alexis KingEq
instance myself, if only to understand what's going on. However, usingEq (m (Either e a))
in the typeclass constraint produces a warning and asks me to turn onUndecidableInstances
. Is it really not possible to write this instance without UndecidableInstances? If so, why? I'm going to edit my question to include this. – WilduckUndecidableInstances
is safe. It allows type class resolution to be recursive in non-total ways. The worst thing that happens is the compiler gets stuck in a recursive loop. If the compiler terminates the resulting code is still safe. It's an extension because a compiler is expected to terminate. – Cirdec