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?

+3


source to share


2 answers


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)

      

+6


source


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.;)

+2


source







All Articles