2011-03-04 77 views
31

在我的空闲时间我正在学习Haskell,所以这是一个初学者的问题。了解如何是Functor的一个实例

在我的阅读我跨越说明如何Either aFunctor实例的例子就是:

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) -> f c -> f d

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

  3. Right (g x)类型为Either a (g x),并且g x类型为d,所以我们有一个的Right (g x)类型是Either a d,这是我们从fmap预期(见上文2)

  4. 现在,如果我们看一下Left (g x)我们可以用同样的理由说,它的类型是Either (g x) b,即Either d b,这不是我们从fmap预期(见2以上):在d应该是第二个参数,不是第一!所以我们不能通过Left来映射。

我的推理是否正确?

+11

要么是可能更明显比算符一个Bifunctor - 一个Bifunctor具有操作的bimap - bimap的::(一 - >米) - >( b→n)→fab→fm n。这使您可以在左右两种情况下进行映射。 Haskell的标准库没有Bifunctor类,这是因为虽然它适用于Either和pair(a,b),但它比“疯子”中的Functors少得多。 – 2011-03-04 16:12:33

+1

3,你使用(g x)作为类型的一部分,但它是一个值。看来你打算在那里写'typeof(g x)',而不是'(g x)'本身。 – Peaker 2011-03-05 23:20:20

+0

@stephen tetley:这很有趣!谢谢 – MarcoS 2011-03-07 08:41:33

回答

20

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

+0

我还没有学到关于monads的信息:)我读过''ab'是以你描述的方式使用的,'a'代表错误信息:但这只是一个约定,对吧? – MarcoS 2011-03-04 15:54:47

+0

@MarcoS:没错,这只是一个惯例。要么可以解释为分离:类型的值或者b包含a类型的值或者b类型的值,但不包括两者。 – 2011-03-04 16:42:38

+0

在个别情况下 - “任一字符串字符串”“任一字符串字符”,“任一字符串真实”等 - 当然是这样;左右类型没有区别。但作为一个仿函数,你可以将其映射为 - (或者是字符串),在这些例子中 - 有完全的不对称。进入具体Functor实例(这里是'String')的类型必须表示可以在任何类型的所有普通计算中发现的一般东西(发生在右边,通过'fmap') - 所以一个'错误'解释虽然不是唯一的解释,但并不意外。 – applicative 2011-03-05 04:37:58

8

您的帐户当然是正确的。也许我们之所以对这样的实例有困难,是因为我们确实一次定义了无数个仿函数实例 - 每种可能的Left类型都有一个实例。但是Functor实例是系统中无限多种类型的系统操作方式。因此,我们正在定义无限多种系统操作系统中无限多种类型的方法。这个实例通过两种方式涉及到普遍性。

如果你分阶段,但也许并不奇怪。首先,这些类型是使用单位类型()及其唯一合法值()Maybe一个冗长的版本:

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值均未触及。

+0

谢谢,这也是一个非常好的解释。然而,我接受FUZXXL的文章作为答案,因为除了证实我的推理之外,它还给我提供了一个有趣的提示,解释'b或者b'为计算(monad) – MarcoS 2011-03-07 08:53:12

1

现在,我试图理解为什么 实现地图的 权值构造的情况下,但不 在左的情况下?

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

假定a =字符串(错误消息) 您将一个应用于浮动。

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

(任一字符串)(浮点)=任一字符串浮点数。

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

换句话说无论是是一个仿函数,因为没有给出这样一个明显的FMAP:

  • 的正确的价值观以F
  • 为左值无能为力
4

正如其他人所提到,Either类型是它的两个参数中的函数。但是在Haskell中,我们能够(直接)在类型的最后一个参数中定义函子。在这样的情况下,我们可以通过使用newtype小号绕过限制:

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

因此,我们必须构造FlipEither :: Either a b -> FlipEither b a一个包装Either到我们newtype与交换的类型参数。我们有破解者unFlipEither :: FlipEither b a -> Either a b解开它。现在,我们可以定义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我们得到的FunctorEither只是定义,只是与Left/Right交换。而现在,只要我们需要Either的第一个类型参数中的Functor实例,我们可以将值包装为FlipEither,然后解包。例如:

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

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

EitherBifunctor定义是非常对称的:

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 
相关问题