![](/img/trans.png)
[英]Why can I not make a (Functor f) => ConcreteType -> f String into an instance Functor ((->) ConcreteType)?
[英]Why can't I make Either instance of Functor using id in Haskell?
在制作我的自定义Either
和Functor
,只是为了理解更清晰的类型和类型类,我发现了以下情况:
Functor
module Functor (Functor, fmap) where
import Prelude hiding(Functor, fmap)
class Functor f where
fmap :: (a -> b) -> f a -> f b
Either
module Either(Either(..)) where
import Prelude hiding(Either(..), Functor, fmap)
data Either a b = Left a | Right b deriving(Show)
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ (Left x) = Left x
上面显示的代码编译得很好但是,如果我将其更改为使用id
,则不会编译:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ = id
为什么?? 我错过了什么? 以下代码也不起作用:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f all@(Left x) = all
...这在我看来非常奇怪,因为下面的代码编译:
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
data Point = Point Float Float deriving (Show)
test :: Shape -> String
test (Circle _ x) = show x
test all@(Rectangle _ x) = show all ++ " - "++ show x
先感谢您
让我们看一下专门用于Either
仿函数的fmap类型:
fmap :: (a -> b) -> Either e a -> Either e b
正如我们由此可以看出,在fmap f all@(Left _)
类型all
是Either ea
。 这与预期的结果类型不匹配。由fmap
的签名规定的Either eb
,因此fmap f all@(Left _) = all
不是很好的类型。
对于使用id
的情况也是如此。
你要做的是归结为:
f :: Either a Bool -> Either a ()
f (Right _) = Right ()
f left = left
有错误:
foo.hs:3:7:
Couldn't match type ‘Bool’ with ‘()’
Expected type: Either a ()
Actual type: Either a Bool
In the expression: left
In an equation for ‘f’: f left = left
Failed, modules loaded: none.
left
绑定到函数参数。 所以类型检查器知道它的类型Either a Bool
。 然后它被用作返回值。 我们从类型f :: Either a Bool -> Either a ()
知道f :: Either a Bool -> Either a ()
得知返回值必须是Either a ()
类型。 如果left
是有效的返回值,则其类型必须与f
的返回类型匹配。 所以Either a ()
必须等于Either a Bool
; 它不是,因此类型检查器拒绝该程序。
反过来,它基本上是这个问题:
λ let l = Left () :: Either () ()
l :: Either () ()
λ l
Left ()
it :: Either () ()
λ l :: Either () Bool
<interactive>:10:1:
Couldn't match type ‘()’ with ‘Bool’
Expected type: Either () Bool
Actual type: Either () ()
In the expression: l :: Either () Bool
In an equation for ‘it’: it = l :: Either () Bool
我们给了l
一个绑定和一个类型,然后尝试将它作为一个不同的类型。 这是无效的(通过id
提供它也不会改变它的类型)。 尽管Left ()
也是类型为Either () Bool
的值的有效源代码文本 ,但这并不意味着已知可以使用源文本Left ()
定义的类型为Either () ()
的特定值Left ()
可以像使用类型Either () Bool
。
如果您有多态值,则可以执行以下操作:
λ let l = Left ()
l :: Either () b
λ l :: Either () ()
Left ()
it :: Either () ()
λ l :: Either () Bool
Left ()
it :: Either () Bool
注意,这里的原始l
值在b
是多态的; 它可以用作任何 b的Either () b
b。
但是你的fmap
案例却略有不同。 函数 fmap
在b
是多态的,但其参数的值是“在多态的范围内”; 在你有你的论证的时候,类型b
被fmap的调用者选择为某种特定类型,所以它是“某种未知的类型,可以通过任何东西”,而不是“我想选择的任何类型”。 有没有办法以某种方式把类型的值, Either ab
成类型的值Either ac
,所以你要提取a
值,然后创建一个Either ac
包含它。
在解释类型错误方面,我没有任何内容可以添加到前两个答案中,但是我想提一下, Left x :: Either ta
在内存中的表示方式与Left x :: Either tb
。 这意味着即使类型系统不允许您使用Either ta
代替类型为Either tb
的值,由于已经通过其他答案完全清楚地解释的原因,您可以通过类型检查器“强制”它使用unsafeCoerce
:
import Unsafe.Coerce (unsafeCoerce)
instance Functor (Either t) where
fmap f (Right a) = Right (f a)
fmap f l = unsafeCoerce l
即使unsafeCoerce
通常被认为是要避免的东西,如果你知道自己在做什么,并且你有充分的理由这样做,比如性能, unsafeCoerce
可以让编译器知道你确定运行时值会有用匹配预期的结构。
在这种情况下,没有unsafeCoerce
,并且没有考虑任何潜在的GHC优化, fmap f (Left x) = Left x
将始终构造一个新的但物理上相同的Left
值,而unsafeCoerce
flavor只返回原始Left
值而没有额外的内存分配。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.