我正在尝试Eq为一个新类型编写一个实例EitherT:
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
Run Code Online (Sandbox Code Playgroud)
我假设以下Eq实例可行:
instance (Eq e, Eq a, Eq m) => Eq (EitherT e m a) where
a == b = (runEitherT a) == (runEitherT b)
Run Code Online (Sandbox Code Playgroud)
但是,我看到一个错误:
Expected kind '* -> *', but 'm' has kind '*'
Run Code Online (Sandbox Code Playgroud)
我从那个错误中读到的是,我的类型类约束( ... Eq m) => ...让编译器误以为我认为m是善意的*,当我的newtype声明EitherT期望它是善良的时候* -> *.
我想知道我需要做什么,声明我想要一个Eq更高的kinded类型的实例m来实现Eq我的EitherTnewtype.
编辑:正如@AlexisKing所指出的,我可以使用它:
{-# LANGUAGE UndecideableInstances #-}
instance (Eq (m (Either e a))) => Eq (EitherT e m a) where
a == b = (runEitherT a) == (runEitherT b)
Run Code Online (Sandbox Code Playgroud)
但是,对于我来说,编写此Eq实例需要语言扩展似乎很奇怪.在vanilla Haskell中没有其他方法可以表达这样的类型类约束吗?如果没有,为什么?
您正在寻找Eq1这是Data.Functor.Classes因为基地4.9.0.0.在此之前它是在其中一个-extras包中还是transformers?(自0.4.0.0起,它现在在变形金刚中)
Eq1 f说你可以比较fs,只要你有办法比较他们的内容
class Eq1 f where
liftEq :: (a -> b -> Bool) -> f a -> f b -> Bool
Run Code Online (Sandbox Code Playgroud)
在你的情况下,你会像它一样使用它
instance (Eq e, Eq1 m) => Eq1 (EitherT e m) where
liftEq f a b = liftEq (liftEq f) (runEitherT a) (runEitherT b)
Run Code Online (Sandbox Code Playgroud)
的liftEq f是使用现有Eq1的实例Either.
并且可以将Eq实例定义为
instance (Eq e, Eq a, Eq1 m) => Eq (EitherT e m a) where
(==) = liftEq (==)
Run Code Online (Sandbox Code Playgroud)
老Eq1了
class Eq1 f where
eq1 :: (Eq a) => f a -> f a -> Bool
Run Code Online (Sandbox Code Playgroud)
在你的情况下,你会像它一样使用它
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)
Run Code Online (Sandbox Code Playgroud)