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定义在类型检查方面表现不同.
如果我们给构造函数一个不同的名称,以便区分类型级别和值级别,可能会更清楚:
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.