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. - WilduckStandaloneDerivingandUndecidableInstancesand 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 KingEqinstance 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. - WilduckUndecidableInstancesis 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