为什么这个仿函数定义被拒绝了?
data Second a b = Ok a b | Ko a b deriving (Show)
instance Functor (Second x) where
fmap f (Ok a b ) = Ok (f a) b
fmap f (Ko a b ) = Ko a (f b)
GHCi, version 8.0.1
main.hs:4:22: error:
• Couldn't match type ‘b’ with ‘x’
‘b’ is a rigid type variable bound by
the type signature for:
fmap :: forall a b. (a -> b) -> Second x a -> Second x b
at main.hs:4:3
‘x’ is a rigid type variable bound by
the instance declaration at main.hs:3:10
Expected type: Second x b
Actual type: Second b a
• In the expression: Ok (f a) b
In an equation for ‘fmap’: fmap f (Ok a b) = Ok (f a) b
In the instance declaration for ‘Functor (Second x)’
• Relevant bindings include
a :: x (bound at main.hs:4:14)
f :: a -> b (bound at main.hs:4:8)
fmap :: (a -> b) -> Second x a -> Second x b (bound at main.hs:4:3)
main.hs:4:28: error:
• Couldn't match expected type ‘a’ with actual type ‘x’
‘x’ is a rigid type variable bound by
the instance declaration at main.hs:3:10
‘a’ is a rigid type variable bound by
the type signature for:
fmap :: forall a b. (a -> b) -> Second x a -> Second x b
at main.hs:4:3
• In the first argument of ‘f’, namely ‘a’
In the first argument of ‘Ok’, namely ‘(f a)’
In the expression: Ok (f a) b
• Relevant bindings include
b :: a (bound at main.hs:4:16)
a :: x (bound at main.hs:4:14)
f :: a -> b (bound at main.hs:4:8)
fmap :: (a -> b) -> Second x a -> Second x b (bound at main.hs:4:3)
这一切意味着什么?请帮忙。
答案 0 :(得分:6)
如果您解压缩Functor
的定义,您会看到Second
的第一个参数保持不变,而其第二个参数可用于转换。
class Functor f where
fmap :: (s -> t) -> f s -> f t
(我已将类型变量a
和b
分别重命名为s
和t
,因为您还有值变量a
和b
。)
class
在您的具体instance
instance Functor (Second x) where
-- fmap :: (s -> t) -> (Second x) s -> (Second x) t
-- i.e. (s -> t) -> Second x s -> Second x t
现在,当您实施fmap
时,您必须确保fmap
的用户可以选择任何x
,s
和他们想要t
。所以你不必对它们做任何假设:它们代表任意的,可能是不同的类型。这就是当他们谈论"刚性"时错误信息的含义。类型变量:您的代码不允许为它们选择特定类型,以便其用户可以。编译器抱怨你承诺一个非常多态的函数,但只提供一个较少的多态函数,只有在x=s=t
时才会检查。
也就是说,当你写
时 fmap f (Ok a b) = Ok (f a) b
你有
f :: s -> t
Ok a b :: Second x s
a :: x
b :: s
你要回来了
Ok (f a) b :: Second x t
需要
f a :: x -- clearly not true, as f :: s -> t
b :: t -- clearly not true, as b :: s
f a
需要
a :: s -- clearly not true, as a :: x
所以,是的,很多错误。
Functor
实例允许您转换与类型 last 参数对应的位置的数据,即
b
data Second a b = Ok a b | Ko a b deriving (Show)
所以你的
fmap f (Ko a b ) = Ko a (f b)
很好,但你的
fmap f (Ok a b ) = Ok (f a) b
使用不同的参数,但不行。
fmap f (Ok a b ) = Ok a (f b)
会奏效。将data
声明更改为
data Second a b = Ok b a | Ko a b deriving (Show)
并保留instance
原样。
要么修复有效,要么不要同时做到"只是为了安全起见"!