首页 文章

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

提问于
浏览
3

我正在尝试为 EitherT newtype写一个 Eq 实例,由下式给出:

newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }

我假设以下 Eq 实例可行:

instance (Eq e, Eq a, Eq m) => Eq (EitherT e m a) where
  a == b = (runEitherT a) == (runEitherT b)

但是,我看到一个错误:

Expected kind '* -> *', but 'm' has kind '*'

我正在从那个错误中读到的是,我的类型类约束 ( ... Eq m) => ... 让编译器误以为我认为 m 是善良的 * ,当我的 EitherT 的newtype声明期望它是善良的 * -> * 时 .

我想知道我需要做什么,声明我想要一个更高的kinded类型 mEq 实例来为我的 EitherT newtype实现 Eq .

Edit: 正如@AlexisKing指出的那样,我可以使用它:

{-# LANGUAGE UndecideableInstances #-}
instance (Eq (m (Either e a))) => Eq (EitherT e m a) where
  a == b = (runEitherT a) == (runEitherT b)

但是,对于我来说,编写这个 Eq 实例需要语言扩展似乎很奇怪 . 在vanilla Haskell中没有其他方法可以表达这样的类型类约束吗?如果没有,为什么?

2 回答

  • 6

    你正在寻找 Eq1 ,它位于Data.Functor.Classes,因为基数为4.9.0.0 . 在此之前它是 -extras 包中的一个或 transformers ? (这是in transformers now since 0.4.0.0

    Eq1 f 表示你可以比较 f 只要你有办法比较它们的内容

    class Eq1 f where
        liftEq :: (a -> b -> Bool) -> f a -> f b -> Bool
    

    在你的情况下,你会像它一样使用它

    instance (Eq e, Eq1 m) => Eq1 (EitherT e m) where
       liftEq f a b = liftEq (liftEq f) (runEitherT a) (runEitherT b)
    

    liftEq f 将使用 Eq1 的现有 Eq1 实例 .

    并且可以将 Eq 实例定义为

    instance (Eq e, Eq a, Eq1 m) => Eq (EitherT e m a) where
       (==) = liftEq (==)
    

    旧的 Eq1

    class Eq1 f where
        eq1 :: (Eq a) => f a -> f a -> Bool
    

    在你的情况下,你会像它一样使用它

    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)
    
  • 2

    值得注意的是,这个实例已经存在于 either 包的当前版本中(尽管不是旧的 EitherT 包,它被认为是过时的):

    instance Eq (m (Either e a)) => Eq (EitherT e m a) where
      (==) = (==) on runEitherT
    

    当然,正如@Alexis King所说,它需要 UndecidableInstances ,但 either 包是由Edward Kmett创作的,他是臭名昭着的dilettante和业余爱好者,他们不能像我们真正的程序员那样编写适当的Haskell98 . ;)

相关问题