Mar*_*coS 36 haskell functor typeclass either
在我的空闲时间,我正在学习Haskell,所以这是一个初学者的问题.
在我的阅读中,我遇到了一个例子,说明如何Either a
成为一个实例Functor
:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x
Run Code Online (Sandbox Code Playgroud)
现在,我试图理解为什么实现在Right
值构造函数的情况下映射,但是在不是的情况下Left
?
这是我的理解:
首先让我重写上面的例子
instance Functor (Either a) where
fmap g (Right x) = Right (g x)
fmap g (Left x) = Left x
Run Code Online (Sandbox Code Playgroud)
现在:
我知道 fmap :: (c -> d) -> f c -> f d
如果我们替换f
,Either a
我们得到fmap :: (c -> d) -> Either a c -> Either a d
类型Right (g x)
是Either a (g x)
,和类型g x
的d
,所以我们有一个类型Right (g x)
的Either a d
,这是我们期望从fmap
(见上述2)
现在,如果我们看一下,Left (g x)
我们可以使用相同的推理来说它的类型是Either (g x) b
,也就是说Either d b
,它不是我们所期望的fmap
(参见上面的2.):d
应该是第二个参数,而不是第一个!所以我们无法映射Left
.
我的推理是否正确?
fuz*_*fuz 21
这是正确的.此行为还有另一个非常重要的原因:您可以将其Either a b
视为可能成功的计算,并返回b
或失败并显示错误消息a
.(这也是monad实例的工作方式).因此,函数实例不会触及Left
值,这是很自然的,因为您想要映射计算,如果失败,则无需操作.
app*_*ive 12
您的帐户当然是对的.也许我们对这样的实例有困难的原因是我们实际上一次定义了无限多个函子实例 - 每个可能Left
类型一个.但Functor实例是在系统中无限多种类型上运行的系统方法.因此,我们定义了无数多种系统地操作系统中无限多种类型的方法.该实例涉及两种方式的一般性.
但是,如果你分阶段采取它,也许它不是那么奇怪.这些类型中的第一个是Maybe
使用单位类型()
及其唯一合法值的longwinded版本()
:
data MightBe b = Nope () | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b = Else Int | Value b
Run Code Online (Sandbox Code Playgroud)
在这里,我们可能会厌倦并做一个抽象:
data Unless a b = Mere a | Genuine b
Run Code Online (Sandbox Code Playgroud)
现在我们无问题地制作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)
Run Code Online (Sandbox Code Playgroud)
但是,再次,为什么要打扰,让我们进行抽象:
instance Functor (Unless a) where
fmap f (Mere a) = Mere a
fmap f (Genuine x) = Genuine (f x)
Run Code Online (Sandbox Code Playgroud)
该Mere a
条款将不会受到影响,因为()
,String
和Int
值均未触及.
正如其他人提到的那样,Either
类型在这两个参数中都是一个函子。但是在Haskell中,我们只能(直接)在类型的最后一个参数中定义函子。在这种情况下,我们可以使用newtype
s 来解决限制:
newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }
Run Code Online (Sandbox Code Playgroud)
因此,我们有一个构造函数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)
Run Code Online (Sandbox Code Playgroud)
请注意,如果忘记FlipEither
了一会儿,我们只获得了/ 的Functor
for 定义。现在,每当我们需要第一个类型参数中的实例时,我们都可以将值包装到其中,然后再将其解包。例如:Either
Left
Right
Functor
Either
FlipEither
fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither
Run Code Online (Sandbox Code Playgroud)
更新:查看Data.Bifunctor,其中Either
和(,)
是实例。每个bifunctor都有两个参数,并且每个参数都是一个函子。这反映在Bifunctor
的方法first
和中second
。
的定义Bifunctor
中Either
是非常对称的:
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
Run Code Online (Sandbox Code Playgroud)