具有更高kinded类型的类型类约束

Wil*_*uck 3 haskell

我正在尝试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中没有其他方法可以表达这样的类型类约束吗?如果没有,为什么?

Cir*_*dec 6

您正在寻找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)

  • 如果你想编写"标准"Haskell,那么`Eq1`只是必须的,我绝不会推荐.没有扩展名的Haskell不是一种非常有用的语言. (2认同)