Haskell的< |>是什么?操作员呢?

时间:2014-09-23 18:39:19

标签: haskell applicative

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

<|>函数的情况一样。

我给的全是:(<|>) :: f a -> f a -> f a并且它是“关联二进制操作” ......

在检查Control.Applicative后,我知道根据实施情况,它确实看似无关紧要。

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

好的,所以如果没有剩下它就会返回,否则它会返回left,gotcha ..这让我相信它是一个“左或右”的运算符,鉴于它使用了|,这有点合理和|的历史用法为“OR”

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

除此之外它只是调用list的连接运算符......打破我的想法......

那究竟是什么功能呢?有什么用?在宏观的计划中它适合哪里?

4 个答案:

答案 0 :(得分:65)

通常它意味着“选择”或“平行”,因为a <|> babab的“选择”并行完成。但是让我们回来吧。

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

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

其他法律包括与empty的身份。特别是a <|> empty = empty <|> a = a。在我们对“选择”或“平行”的直觉中,这些法律读作“一个或(不可能的东西)必须是一个”或“一个旁边(空的过程)只是一个”。它表示emptyAlternative的某种“失败模式”。

还有其他法律规定了(<|>) / emptyfmap(来自Functor)或pure / (<*>)(来自{{}的互动方式{1}}),但也许是理解Applicative含义的最佳方法是检查实例化(<|>)的类型的一个非常常见的例子:a Alternative。< / p>

如果Parserx :: Parser A,则y :: Parser B按顺序解析(,) <$> x <*> y :: Parser (A, B) 然后 x。相比之下,y(fmap Left x) <|> (fmap Right y)开始解析 xy,以尝试两种可能的解析。换句话说,它表示您的解析树,选择或并行解析Universe中的分支

答案 1 :(得分:35)

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

它需要两个f a值,并且必须返回一个值。所以它必须以某种方式组合或选择其输入。它在a类型中具有多态性,因此它将完全无法检查a内可能包含的f a类型的值;这意味着它不能做&#34;组合&#34;通过组合a值,所以它必须纯粹根据类型构造函数f添加的任何结构。

这个名字也有点帮助。某种&#34; OR&#34;确实是作者试图用名称&#34; Alternative&#34;和符号&#34;&lt; |&gt;&#34;。

现在,如果我有两个Maybe a值并且我必须将它们合并,我该怎么办?如果他们同时Nothing 以返回Nothing,则无法创建a。如果其中至少有一个是Just ...,我可以按原样返回我的一个输入,或者我可以返回Nothing。很少有函数甚至是可能的类型为Maybe a -> Maybe a -> Maybe a,而且对于名称为&#34的类;替代&#34;给出的是非常合理和明显的。

如何合并两个[a]值?这里有更多可能的功能,但实际上很明显它可能会做什么。而名称&#34; Alternative&#34;确实可以为你提供一个很好的提示提供你熟悉标准&#34; nondeterminism&#34; monad / applicative列表的解释;如果您将[a]视为&#34;非确定性a&#34;通过一系列可能的值,然后明显的方法是“结合两个不确定的a值&#34;以一种可能得名的方式&#34; Alternative&#34;是产生一个非确定的a,它可以是任何一个输入的任何值。

对于解析器;结合两个解析器有两个明显的广泛解释,浮现在脑海中;你要么生成一个与第一个匹配的解析器,然后生成第二个匹配器的解析器,要么生成一个与匹配的解析器第一个第二个做什么(当然,每个选项的细微细节都为选项留出了空间)。鉴于名称&#34;替代&#34;,&#34;或&#34;对<|>来说,解释似乎很自然。

因此,从足够高的抽象层次看,这些操作所有&#34;做同样的事情&#34;。类型类实际上是在高抽象级别操作,这些东西都看起来相同&#34;。当我在一个已知的实例上运行时,我只想将<|>操作视为它对该特定类型的操作。

答案 2 :(得分:12)

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

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

答案 3 :(得分:12)

这些似乎非常不同,但请考虑:

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]

Maybe只能包含一个值(或零,但不能包含任何其他金额)。但是,嘿,如果他们都是相同的,为什么你需要两种不同的类型?他们的不同之处在于,不同

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