Typecheck因明显相等的实例定义而失败

Tim*_*enk 3 haskell typechecking

给出以下类型定义

newtype Constant a b = Constant { getConstant :: a }
  deriving (Eq, Show)
Run Code Online (Sandbox Code Playgroud)

这个Functor instance定义是有效的

instance Functor (Constant a) where
  fmap _ (Constant x) = Constant x
Run Code Online (Sandbox Code Playgroud)

而明显等同的instance定义

instance Functor (Constant a) where
  fmap _ x = x
Run Code Online (Sandbox Code Playgroud)

因类型检查错误而失败(摘录)

Expected type: Constant a b
  Actual type: Constant a a1
Run Code Online (Sandbox Code Playgroud)

使用GHC版本8.0.2.

问题是,为什么这两个(显然是等价的)instance定义在类型检查方面表现不同.

lef*_*out 5

如果我们给构造函数一个不同的名称,以便区分类型级别和值级别,可能会更清楚:

newtype Constant a b = ConstVal { getConstVal :: a }
  deriving (Eq, Show)

instance Functor (Constant a) where
  fmap _ (ConstVal x) = ConstVal x
Run Code Online (Sandbox Code Playgroud)

现在,你为什么不写fmap _ x = x

ConstVal是一个多态构造函数:

ConstVal :: a -> Constant a b
Run Code Online (Sandbox Code Playgroud)

...即

ConstVal :: ? a b . a -> Constant a b
Run Code Online (Sandbox Code Playgroud)

虽然这个通用量词在Haskell中是可选的,但它实际上很重要.ConstVal基本上有两个额外的类型级参数.换句话说,这不仅仅是一个构造函数,而是整个构造函数家族,比如

ConstValBoolBool :: Bool -> Constant Bool Bool
ConstValBoolInt  :: Bool -> Constant Bool Int
ConstValBoolChar :: Bool -> Constant Bool Char
...
ConstValCharBool :: Char -> Constant Char Bool
ConstValCharInt  :: Char -> Constant Char Int
ConstValCharChar :: Char -> Constant Char Char
...
...
Run Code Online (Sandbox Code Playgroud)

所有这些实际上共享相同的值级别名称ConstVal,但对于类型系统,它们都是不同的.明确写出来,你就有了

fmapBoolStringInt :: (String -> Int) -> Constant Bool String -> Constant Bool Int
fmapBoolStringInt _ (ConstValBoolString x) = ConstValBoolInt x
Run Code Online (Sandbox Code Playgroud)

这里很清楚,双方的价值实际上并不相同,因此无法减少fmapBoolStringInt _ x = x.