无法将类型“a”与“a1”匹配

Couldn't match type `a` with `a1`

我正在做 Haskell Programming from First Principles, Chapter 16 中的练习。问题是要求我们为数据类型编写 Functor 定义:

{-# LANGUAGE FlexibleInstances #-}
newtype Flip f a b = Flip (f b a) deriving (Eq, Show)
newtype K a b = K a
instance Functor (Flip K a) where
  fmap = undefined

这是我的尝试:

{-# LANGUAGE FlexibleInstances #-}
newtype Flip f a b =
  Flip (f b a)
  deriving (Eq, Show)

newtype K a b =
  K a

-- instance Functor (K a) where
-- fmap _ (K a) = K a

instance Functor (Flip K a) where
  fmap _ (Flip (K a)) = Flip (K a)

但无法编译:

chapEx2.hs:17:25: error:
    • Couldn't match type ‘a1’ with ‘b’
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> Flip K a a1 -> Flip K a b
        at chapEx2.hs:17:3-6
      ‘b’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> Flip K a a1 -> Flip K a b
        at chapEx2.hs:17:3-6
      Expected type: Flip K a b
        Actual type: Flip K a a1
    • In the expression: Flip (K a)
      In an equation for ‘fmap’: fmap f (Flip (K a)) = Flip (K a)
      In the instance declaration for ‘Functor (Flip K a)’
    • Relevant bindings include
        a :: a1 (bound at chapEx2.hs:17:19)
        f :: a1 -> b (bound at chapEx2.hs:17:8)
        fmap :: (a1 -> b) -> Flip K a a1 -> Flip K a b
          (bound at chapEx2.hs:17:3)
   |
17 |   fmap f (Flip (K a)) = Flip (K a)
   |                         ^^^^^^^^^^

有人可以解释错误消息吗?我只是对错误信息感到困惑:

  1. 为什么编译器将 Actual type 推断为 Flip K a a1 而不是 Flip K a b
  2. 为什么编译器还要去匹配K的第三个参数呢?类型K的定义只有一个a,没有bb只出现在数据类声明的左边(=的左边符号)而不是 newtype K a b = K a.
  3. 的类型类声明(= 符号的右侧)
  4. 为什么Flip (K a)不能改成Flip x
  5. 我发现 fmap f (Flip (K a)) = Flip (K (f a)) 可以编译,有什么区别?

问题 1

你犯了一个错误。 Flip 意味着您需要映射 first 参数 K:

instance Functor (Flip K a) where
  fmap f (Flip (K a)) = Flip (K (f a))

问题 2

这是有充分理由的。其中之一是给类型一个 phantom 参数可能非常有用(维护程序不变量或指导实例解析)。如果编译器忽略它们,这些技术将毫无价值。 (注意:您可以在需要时忽略它们,并且 Data.Coerce 提供了一些这样做的高级工具。您可能还没有准备好进行强制转换)。

另一个原因是,要弄清楚哪些类型与其他类型相等要困难得多,因为您必须查看每种类型的详细信息。可能还有更多原因。

旁白

FlexibleInstances 在这里显得相当尴尬和局限。这是我的做法:

-- A functor in the second to last type argument
class Functor2 p where
  fmap2 :: (a -> a') -> p a b -> p a' b

instance Functor2 K where
  fmap2 = -- you fill in the blank

instance Functor2 p => Functor (Flip p b) where
  fmap = -- you fill in the blank