为什么Either没有替代实例,但有一个类似于Alternative的半群?

17

我是Haskell的新手,我想知道为什么没有Either的替代实例,但有一个表现得像alternative的semigroup:

instance Semigroup (Either a b) where
Left _ <> b = b
a      <> _ = a

这个实例会丢弃或修正“错误”,当两个操作数都被标记为Right时,它会选择第一个。难道这不正是alternative提供的“选择”吗?

我希望半群实例大致如下:

instance (Semigroup b) => Semigroup (Either a b) where
Left e  <> _       = Left e
_       <> Left e  = Left e
Right x <> Right y = Right (x <> y)

这意味着它传播错误并追加常规结果。

我猜我对 Either 或涉及的类型类有错误的概念。


这个看起来有关联:https://ghc.haskell.org/trac/ghc/ticket/9588 - Dietrich Epp
2个回答

18

你会期望 Alternative 实例给你什么?我认为你可以通过观察另一个同时有这两个实例的类型来感受一下 AlternativeSemigroup 的区别,例如 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的情况下,则采取左偏的选项。

那么为什么不能为Either创建一个Alternative实例呢?如果您查看作为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 声明了一个针对 Either eApplicative 实例。正如你所看到的,Either 只是第二个类型变量(在 Either e a 中为 a)的一个适用的函子。因此,Either e 的恒等元素需要 e 也有一个恒等元素。虽然可以构造一个类型,在该类型中 e 具有 Alternative 实例,但是无法为具有该 eEither 创建 Alternative 实例,因为类型类定义中没有这样的约束(类似于:(Alternative e,Applicative (f e)) => Alternative (f e))。

TL;DR:如果我的话让你困惑了,我很抱歉。简单来说,对于 Eitherf 是错误的类型,Alternative 要求 f :: * -> *,而 Either 的类型是 Either :: * -> * -> *

因此,Maybe 可以有 Alternative 实例,因为它的类型是 Maybe : * -> *,并且具有 empty 所需的恒等元素(Nothing)。查看Alternative的所有实例,并注意每个实例数据类型的类型。

你可以使用 :k 在 ghci 中找到数据类型的类型:

λ > :k Maybe
Maybe :: * -> *
λ > :k Either
Either :: * -> * -> *

1
这是一个很棒的答案。感谢您抽出时间! - user6445533
2
抱歉,还有一个问题:Just "a" <> Just "b" == Just "ab",如预期一样,但是 Right "a" <> Right "b" == Right "a"。为什么会有这种差异? - user6445533
1
嗯,所以 Either a bSemigroup 实例实际上并不像你写的那样被定义。它完全独立于是否存在 bSemigroup 实例。当 Left 值是 (<>) 的左项时,它基本上只是丢弃该值,否则返回左项。看看这里的实际源代码 :-) 我不确定这个定义背后的原理是什么,但对于您来说,这非常有效,因为您似乎想要一个 Alternative 实例。 - basile-henry
1
@basile-henry 我不明白为什么 Either 的种类是 * -> * -> * 会有问题。这并不解释为什么 Either 不能成为 Alternative。例如,Monad 也要求类型的种类是 * -> *,但 Either 实现了一个 Monad 实例 —— 它只是部分应用了 Either。例如,这段代码可以编译并按预期工作:https://hastebin.com/duyituzuda.hs 可能不满足规律性法则,我没有检查过。但这是有效的代码,应该解释为什么不选用该选项。 - Shersh
1
你刚刚添加了一个限制条件,即 e 需要有一个身份元素。这是一个完全可以接受的解决方案,我已经提到过它(使用 Alternative 而不是 Monoid 来获取 empty)。我认为在没有这个限制条件的情况下无法做到这一点。我承认描述这一点的句子非常不清楚,我应该尝试更好地重新表述它。 - basile-henry

6

根据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有点像一个二等公民;它是一个高阶的单子,但它与MonoidSemigroup的关系没有被定义,而这两个类别显然且明确地(在文档中)形成了一个层次结构。

我相信在库邮件列表上已经有大量讨论,如果有一些明显的“正确”解决方案,移动到这些方案可能会导致(在最坏的情况下是静默的)破坏。


备选类型类似乎经常引起问题,也因为其基本法则的原因。谢谢! - user6445533

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接