我也遇到了这个问题,我发现 Ramon 和 Mikhail 的回答内容丰富——谢谢!我把它放在答案而不是评论中,因为 500 个字符太短,而且对于代码格式化。
我很难理解什么是协变的,(a -> Int)
并想出了这个反例,显示data K a = K (a -> Int)
可以作为 Functor 的一个实例(反驳 Ramon 的证明)
data K a = K (a -> Int)
instance Functor K where
fmap g (K f) = K (const 0)
如果它编译,它一定是正确的,对吧?;-) 我花了一些时间尝试其他排列。翻转函数只是让它更容易:
-- "o" for "output"
-- The fmapped (1st) type is a function output so we're OK.
data K0 o = K0 (Int -> o)
instance Functor K0 where
fmap :: (oa -> ob) -> (K0 oa) -> (K0 ob)
fmap g (K0 f) = K0 (g . f)
将 Int 转换为类型变量将其简化为第 3.2 节练习 1 第 2 部分:
-- The fmapped (2nd) type argument is an output
data K1 a b = K1 (a -> b)
instance Functor (K1 a) where
fmap :: (b1 -> b2) -> K1 a b1 -> K1 a b2
fmap g (K1 f) = K1 (g . f)
强制 fmapped 类型成为函数的参数是关键......就像米哈伊尔的回答所说,但现在我明白了;-)
-- The fmapped (2nd) type argument is an input
data K2 a b = K2 (b -> a)
instance Functor (K2 o) where
fmap :: (ia -> ib) -> (K2 o ia) -> (K2 o ib)
-- Can't get our hands on a value of type o
fmap g (K2 f) = K2 (const (undefined :: o))
-- Nor one of type ia
fmap g (K2 f) = K2 (const (f (undefined :: ia)))