通过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的连接运算符......打破我的想法......
那究竟是什么功能呢?有什么用?在宏观的计划中它适合哪里?
答案 0 :(得分:65)
通常它意味着“选择”或“平行”,因为a <|> b
是a
或b
或a
和b
的“选择”并行完成。但是让我们回来吧。
实际上,像(<*>)
或(<|>)
这样的类别操作没有实际意义。这些操作以两种方式赋予意义:(1)通过法则和(2)通过实例化。如果我们不是在谈论Alternative
的特定实例,那么只有(1)可用于直觉意义。
所以“关联”意味着a <|> (b <|> c)
与(a <|> b) <|> c
相同。这很有用,因为它意味着我们只关心与(<|>)
链接在一起的事物的序列,而不是它们的“树结构”。
其他法律包括与empty
的身份。特别是a <|> empty = empty <|> a = a
。在我们对“选择”或“平行”的直觉中,这些法律读作“一个或(不可能的东西)必须是一个”或“一个旁边(空的过程)只是一个”。它表示empty
是Alternative
的某种“失败模式”。
还有其他法律规定了(<|>)
/ empty
与fmap
(来自Functor
)或pure
/ (<*>)
(来自{{}的互动方式{1}}),但也许是理解Applicative
含义的最佳方法是检查实例化(<|>)
的类型的一个非常常见的例子:a Alternative
。< / p>
如果Parser
和x :: Parser A
,则y :: Parser B
按顺序解析(,) <$> x <*> y :: Parser (A, B)
然后 x
。相比之下,y
从(fmap Left x) <|> (fmap Right y)
开始解析 x
或y
,以尝试两种可能的解析。换句话说,它表示您的解析树,选择或并行解析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
只能包含一个值(或零,但不能包含任何其他金额)。但是,嘿,如果他们都是相同的,为什么你需要两种不同的类型?他们的不同之处在于,与不同。
总之,实现可能看起来完全不同,但实际上它们非常相似。