Typical constraints with higher types
I am trying to write an instance Eq
for EitherT
newtype given by:
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
I assumed the following instance Eq
would work:
instance (Eq e, Eq a, Eq m) => Eq (EitherT e m a) where
a == b = (runEitherT a) == (runEitherT b)
However, I see an error:
Expected kind '* -> *', but 'm' has kind '*'
What I am reading from this error is that my type constraint is ( ... Eq m) => ...
confusing the compiler into believing that I deem m
kind *
when my newtype declaration for EitherT
expects it to be kind * -> *
.
I am wondering what I need to do, declare that I need an instance Eq
for a higher type m
to implement Eq
for my EitherT
newtype.
Edit: As @AlexisKing pointed out, 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 that this instance Eq
requires a language extension. Is there no other way to express such a type constraint in vanilla Haskell? If not, why not?
source to share
You are looking for Eq1
which is Data.Functor.Classes
from base 4.9.0.0. Before that, he was in one of the packages -extras
or transformers
? (he's in transformers now with 0.4.0.0 )
Eq1 f
says you can compare f
as long as you have a way to compare their contents.
class Eq1 f where
liftEq :: (a -> b -> Bool) -> f a -> f b -> Bool
In your case, you would use it like
instance (Eq e, Eq1 m) => Eq1 (EitherT e m) where
liftEq f a b = liftEq (liftEq f) (runEitherT a) (runEitherT b)
liftEq f
- use an existing instance Eq1
for Either
.
And can define an instance Eq
as
instance (Eq e, Eq a, Eq1 m) => Eq (EitherT e m a) where
(==) = liftEq (==)
Old Eq1
was
class Eq1 f where
eq1 :: (Eq a) => f a -> f a -> Bool
In your case, you would use it like
instance (Eq e, Eq1 m) => Eq1 (EitherT e m) where
eq1 a b = eq1 (runEitherT a) (runEitherT b)
instance (Eq e, Eq a, Eq1 m) => Eq1 (EitherT e m) where
a == b = eq1 (runEitherT a) (runEitherT b)
source to share
It might be worth noting that this instance already exists in the current versions of the package either
(although not in the old EitherT
package, which is considered obsolete):
instance Eq (m (Either e a)) => Eq (EitherT e m a) where
(==) = (==) on runEitherT
Of course, as @Alexis King pointed out, it is required for it UndecidableInstances
, but the package is either
created by Edward Kemt, the notorious dilettante and hobbyist who cannot write suitable Haskell98 like us real programmers.;)
source to share