繁体   English   中英

为什么我不能在Haskell中使用id创建Functor实例?

[英]Why can't I make Either instance of Functor using id in Haskell?

在制作我的自定义EitherFunctor ,只是为了理解更清晰的类型和类型类,我发现了以下情况:

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 _)类型allEither 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案例却略有不同。 函数 fmapb是多态的,但其参数的值是“在多态的范围内”; 在你有你的论证的时候,类型bfmap的调用者选择为某种特定类型,所以它是“某种未知的类型,可以通过任何东西”,而不是“我想选择的任何类型”。 有没有办法以某种方式把类型的值, 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM