繁体   English   中英

了解Either如何是Functor的一个实例

[英]Understanding how Either is an instance of Functor

在我的空闲时间我正在学习Haskell,所以这是一个初学者的问题。

在我的阅读中,我遇到了一个例子,说明了如何将Either a作为Functor一个实例:

instance Functor (Either a) where
    fmap f (Right x) = Right (f x)
    fmap f (Left x) = Left x

现在,我试图理解为什么实现在Right值构造函数的情况下映射,但是在Left的情况下不是?

这是我的理解:

首先让我重写上面的例子

instance Functor (Either a) where
    fmap g (Right x) = Right (g x)
    fmap g (Left x) = Left x

现在:

  1. 我知道fmap :: (c -> d) -> fc -> fd

  2. 如果我们替代fEither a我们得到fmap :: (c -> d) -> Either ac -> Either ad

  3. 类型Right (gx)Either a (gx)和类型gxd ,所以我们有一个类型Right (gx)Either ad ,这是我们期望从fmap (见上述2)

  4. 现在,如果我们看一下Left (gx)我们可以使用相同的推理来说它的类型是Either (gx) b ,那就是fmap Either db ,这不是我们对fmap期望(参见上面的2.): d应该是第二个参数,而不是第一个! 所以我们无法映射到Left

我的推理是否正确?

这是正确的。 此行为还有另一个非常重要的原因:您可以将Either ab视为计算,可能会成功并返回b或失败并显示错误消息a (这也是monad实例的工作方式)。 所以很自然,仿函数实例不会触及Left值,因为你想要映射计算,如果它失败了,就没有什么可以操作了。

您的帐户当然是对的。 也许我们对这样的实例有困难的原因是我们实际上一次定义了无限多个函子实例 - 每个可能的Left类型一个。 但Functor实例是在系统中无限多种类型上运行的系统方法。 因此,我们定义了无数多种系统地操作系统中无限多种类型的方法。 该实例涉及两种方式的一般性。

但是,如果你分阶段采取它,也许它不是那么奇怪。 这些类型中的第一个是使用unit type ()及其唯一合法值()Maybe的longwinded版本:

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

在这里,我们可能会厌倦并做一个抽象:

data Unless a b    = Mere a     | Genuine b

现在我们无问题地制作Functor实例,第一个看起来很像Maybe的实例:

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

但是,再次,为什么要打扰,让我们进行抽象:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

Mere a触及术语,因为未触及()StringInt值。

正如其他人提到的, Either类型都是两个参数中的函子。 但是在Haskell中,我们能够(直接)在类型的最后一个参数中定义函子。 在这种情况下,我们可以通过使用newtype来解决这个问题:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

因此,我们有构造FlipEither :: Either ab -> FlipEither ba一个包装Either到我们newtype与交换的类型参数。 我们有dectructor unFlipEither :: FlipEither ba -> Either ab将它展开回来的unFlipEither :: FlipEither ba -> Either ab 现在我们可以在FlipEither的最后一个参数中定义一个FlipEither函数实例,这实际上是Either的第一个参数:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

请注意,如果我们忘记FlipEither一段时间,我们只得到Functor for Either的定义,只需使用Left / Right交换。 现在,每当我们需要在Either的第一个类型参数中Either Functor实例时,我们可以将值包装到FlipEither并在之后解包。 例如:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

更新:看看Data.Bifunctor ,其中Either(,)是其实例。 每个bifunctor都有两个参数,并且每个参数都是一个仿函数。 这反映在Bifunctor的方法firstsecond

定义BifunctorEither是非常对称的:

instance Bifunctor Either where
    bimap f _ (Left a)  = Left (f a)
    bimap _ g (Right b) = Right (g b)

    first  f = bimap f id

    second f = bimap id f

现在,我试图理解为什么实现在Right值构造函数的情况下映射,但是在Left的情况下不是?

插入这里,它可能是有道理的。

假设a = String(错误消息)您将A或a应用于Float。

所以你有一个f:Float - > Integer比如说舍入。

(字符串)(Float)= String Float。

now(fmap f):: String Float - >要么是字符串Int所以你要用f做什么? f不知道如何处理字符串,所以你不能在那里做任何事情。 显然 ,你唯一可以采取行动的是正确的值,同时保持左值不变。

换句话说,要么a是一个仿函数,因为有这样一个明显的f​​map给出:

  • for right values适用于f
  • 因为左边的值什么都不做

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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