73

浏览 Haskell 的文档对我来说总是有点痛苦,因为你获得的关于一个函数的所有信息通常只是:f a -> f [a]这可能意味着任何数量的东西。

就像<|>函数的情况一样。

我得到的只是这个:(<|>) :: f a -> f a -> f a它是一个“关联二元运算” ......

经过检查,Control.Applicative我了解到它会根据实施情况做看似无关的事情。

instance Alternative Maybe where
    empty = Nothing
    Nothing <|> r = r
    l       <|> _ = l

好的,所以如果没有左则它返回右,否则它返回左,明白了.. 这让我相信它是一个“左或右”运算符,考虑到它使用|and|的历史用法为“或”,这有点道理"

instance Alternative [] where
    empty = []
    (<|>) = (++)

除了这里它只是调用列表的连接运算符......打破我的想法......

那么这个功能到底是什么?它有什么用?它在宏伟的计划中处于什么位置?

4

4 回答 4

76

通常它的意思是“选择”或“并行”,因为a <|> b它要么是“选择”,要么是a并行完成。但是,让我们备份。bab

(<*>)确实,像or之类的类型类中的操作没有实际意义(<|>)。这些操作以两种方式赋予意义:(1)通过法律和(2)通过实例化。如果我们不是在谈论一个特定的实例,Alternative那么只有 (1) 可用于直觉意义。

所以“关联”意味着a <|> (b <|> c)(a <|> b) <|> c. 这很有用,因为这意味着我们只关心与链接在一起的事物的顺序(<|>),而不是它们的“树结构”。

其他法律包括身份empty。特别是,a <|> empty = empty <|> a = a。在我们对“选择”或“并行”的直觉中,这些定律读作“一个或(不可能的事情)必须是一个”或“一个并列(空过程)只是一个”。它表明这empty是某种“故障模式” Alternative

还有其他关于如何(<|>)/emptyfmap(from Functor) 或pure/ (<*>)(from Applicative) 交互的定律,但也许进一步理解 of 含义的最佳方法(<|>)是检查一个非常常见的实例化类型示例Alternative:a Parser

If x :: Parser Aand y :: Parser Bthen 依次(,) <$> x <*> y :: Parser (A, B)解析x 然后 y。相反,(fmap Left x) <|> (fmap Right y)解析 x,以y开头x,尝试两种可能的解析。换句话说,它表示解析树中的一个分支、一个选择或一个并行解析域。

于 2014-09-23T18:53:24.227 回答
38

(<|>) :: f a -> f a -> f a实际上告诉你很多,即使没有考虑法律Alternative

它需要两个f a值,并且必须返回一个。所以它必须以某种方式组合或从它的输入中选择。它在 type 中是多态的a,因此它将完全无法检查 type 中a可能存在的任何值f a; 这意味着它不能通过组合值来进行“组合” a,因此它必须纯粹根据类型构造函数f添加的任何结构来进行。

这个名字也有点帮助。某种“OR”确实是作者试图用名称“Alternative”和符号“<|>”来表示的模糊概念。

现在,如果我有两个Maybe a值并且必须将它们组合起来,我该怎么办?如果他们都是Nothing我将不得不返回Nothing,没有办法创建一个a. 如果其中至少一个是 aJust ...我可以按原样返回我的输入之一,或者我可以返回Nothing. 很少有甚至可以使用 type 的函数,Maybe a -> Maybe a -> Maybe a对于名称为“Alternative”的类,给出的函数非常合理和明显。

如何结合两个[a]值?这里有更多可能的功能,但实际上很明显这可能会做什么。如果您熟悉列表 monad/applicative 的标准“非确定性”解释,“Alternative”这个名称确实可以很好地暗示这可能是什么;如果您将 a[a]视为具有a可能值的集合的“非确定性”,那么以a一种可能值得称为“替代”的方式“组合两个非确定性值”的明显方法是产生一个a可以是任何值的非确定性从任一输入。

对于解析器;结合两个解析器有两个明显的广泛解释,让人想到;要么你生成一个解析器来匹配第一个所做的事情,然后是第二个所做的事情,或者你生成一个解析器来匹配一个所做的事情第二个所做的事情(当然,这些选项中的每一个都有一些微妙的细节会留下选择余地)。鉴于“替代”这个名称,“或”的解释对于<|>.

因此,从足够高的抽象层次来看,这些操作“做同样的事情”。类型类实际上是为了在这些东西“看起来都一样”的高抽象级别上运行。当我在单个已知实例上操作时,我只是认为该<|>操作与它对特定类型所做的完全一样。

于 2014-09-24T05:08:19.480 回答
14

Alternative一个不是解析器或类似 MonadPlus 的东西的有趣示例是 ,它是包中Concurrently非常有用的类型async

因为Concurrently,empty是一个永远持续的计算。并(<|>)同时执行其参数,返回第一个完成的结果,并取消另一个。

于 2014-09-23T21:22:38.383 回答
14

这些看起来非常不同,但请考虑:

Nothing <|> Nothing == Nothing
     [] <|>      [] ==      []

Just a  <|> Nothing == Just a
    [a] <|>      [] ==     [a]

Nothing <|> Just b  == Just b
     [] <|>     [b] ==     [b]

所以......这些实际上非常非常相似,即使实现看起来不同。唯一真正的区别在这里:

Just a  <|> Just b  == Just a
    [a] <|>     [b] ==     [a, b]

AMaybe只能保存一个值(或零,但不能保存任何其他数量)。但是,嘿,如果它们都是相同的,为什么你需要两种不同的类型?他们不同的全部意义在于,你知道,不同

总之,实现可能看起来完全不同,但实际上非常相似。

于 2014-09-24T08:34:27.270 回答