我是一个 Haskell 新手,我想知道为什么
Either
没有替代实例,而是一个半群,其行为正如我所期望的替代实例:
instance Semigroup (Either a b) where
Left _ <> b = b
a <> _ = a
此实例丢弃或纠正“错误”,并且当两个操作数都标有
Right
时,它采用第一个。这不正是替代性提供的“选择”吗?
我希望半群实例大致如下:
instance (Semigroup b) => Semigroup (Either a b) where
Left e <> _ = Left e
_ <> Left e = Left e
Right x <> Right y = Right (x <> y)
这意味着它会传播错误并附加常规结果。
我想我对
Either
或所涉及的类型类有错误的概念。
您希望
Alternative
实例能给您带来什么。我认为让您了解 Alternative
和 Semigroup
有何不同的一个好方法是查看另一种具有两者实例的类型:例如 Maybe String
:
λ > Just "a" <> Just "b"
Just "ab"
λ > Just "a" <> Nothing
Just "a"
λ > Nothing <> Just "b"
Just "b"
λ > Nothing <> Nothing
Nothing
λ > Just "a" <|> Just "b"
Just "a"
λ > Just "a" <|> Nothing
Just "a"
λ > Nothing <|> Just "b"
Just "b"
λ > Nothing <|> Nothing
Nothing
好吧,所以主要区别似乎是
Just "a"
和 Just "b"
。这是有道理的,因为您在 Semigroup
的情况下将它们组合起来,而不是在 Alternative
的情况下采用左偏选项。
现在为什么不能为
Alternative
提供 Either
实例。如果您查看属于 Alternative
类型类的函数:
λ > :i Alternative
class Applicative f => Alternative (f :: * -> *) where
empty :: f a
(<|>) :: f a -> f a -> f a
some :: f a -> f [a]
many :: f a -> f [a]
{-# MINIMAL empty, (<|>) #-}
看起来它定义了一个
empty
的概念;这是 (<|>)
运算符的身份。案例中的身份意味着身份与其他事物之间的选择始终是其他事物。
现在,您将如何为
Either e a
构建身份?如果您查看 Alternative
实例上的约束,您会发现它需要 f
才能拥有 Applicative
实例。没关系,Either
有一个为 Applicative
声明的 Either e
实例。正如您所看到的,Either
只是第二个类型变量上的应用函子(在a
的情况下为Either e a
)。因此 Either e
的恒等式需要 e
也有一个恒等式。虽然可以构造一个类型,其中 e
具有 Alternative
的实例,但您无法使用该 Alternative
为 Either
制作 e
的实例,因为类型类定义中没有这样的约束(类似于:(Alternative e, Applicative (f e)) => Alternative (f e)
)。
TL;DR:如果我的漫无目的失去了你,我很抱歉,缺点是
f
在Either
的情况下是错误的种类,Alternative
需要f :: * -> *
而Either
属于种类Either :: * -> * -> *
因此
Maybe
可以拥有 Alternative
的实例,因为它具有 kind Maybe : * -> *
并且具有 Nothing
所需的身份概念 (empty
)。查看 Alternative
的所有实例,并注意每个实例数据类型的类型。
您可以使用
:k
找到 ghci 中的数据类型:
λ > :k Maybe
Maybe :: * -> *
λ > :k Either
Either :: * -> * -> *
根据上面发布的 ticket Dietrich Epp,
Alternative
的问题是 empty
。如果你有:
instance Alternative (Either a) where ...
您需要能够“凭空”提取一些价值
Either a b
,这是您的身份对象。一种可能的情况可能是:
instance (Monoid a)=> Alternative (Either a) where
empty = Left mempty
...
您还问为什么
Semigroup
实例是这样定义的,坦白说我也不明白。看来您提议的实例也将允许(兼容/合法)Monoid
实例:
instance Monoid b=> Monoid (Either a b) where
mempty = Right mempty
这与
Maybe
实例一致(Maybe 和 Either 之间的代数关系是显而易见的)。
所以情况不太好。部分问题是,如果你愿意的话,
Alternative
可以说是二等舱;它是一个单一的更高种类的东西,但它与 Monoid
和 Semigroup
的关系,显然并明确地(在文档中)形成了一个层次结构,尚未定义。
我确信图书馆邮件列表上已经进行了大量讨论,如果有一些明显的“正确”解决方案,那么转向它们可能会导致(在最坏的情况下无声的)破坏。
虽然
Either
没有 Alternative
实例,但它确实有 Bialternative
实例。
{-# LANGUAGE QuantifiedConstraints #-}
class (Bifunctor p, forall a. Applicative (p a)) => Bialternative p where
{-# MINIMAL left, ((<<|>>) | liftL2) #-}
left :: a -> p a b
(<<|>>) :: p (a -> b) c -> p a c -> p b c
(<<|>>) = liftL2 id
liftL2 :: (a -> b -> c) -> p a d -> p b d -> p c d
liftL2 f a b = f `first` a <<|>> b
(|>>) :: p a c -> p b c -> p b c
a |>> b = liftL2 (const id) a b
(<<|) :: p a c -> p b c -> p a c
a <<| b = liftL2 const a b
这是
Bialternative
的 Either
实例。您可以使用 |>>
代替 <|>
。
instance Bialternative Either where
left :: a -> Either a b
left = Left
(<<|>>) :: Either (a -> b) c -> Either a c -> Either b c
Left f <<|>> Left a = Left (f a)
Right c <<|>> _ = Right c
_ <<|>> Right c = Right c
Bialternative
的实例应满足以下定律。
身份
left id <<|>> v = v
成分
left (.) <<|>> u <<|>> v <<|>> w = u <<|>> (v <<|>> w)
同态
left f <<|>> left x = left (f x)
交汇处
u <<|>> left y = left ($ y) <<|>> u
左抓
pure x <<|>> v = pure x
正确接球
left x <*> v = left x