Haskell的<|>运算符是做什么的?

浏览Haskell的文档对我来说总是有些痛苦,因为你得到的有关函数的所有信息往往只不过是: fa -> f [a]这可能意味着任何事情。

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

我得到的是: (<|>) :: fa -> fa -> fa ,这是一个“联想二元操作”

经过对Control.Applicative检查,我了解到它根据实现做了看起来不相关的事情。

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

好的,所以如果没有剩下它就返回正确的,否则返回左边,gotcha ..这使我相信这是一个“左或右”的运算符,这是有道理的,因为它使用|| 作为“或”的历史用法

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

除了这里,它只是调用列表的连接操作符…打破我的想法…

那么究竟是什么function呢? 它有什么用途? 它在哪里适合在事物的macros伟计划?

典型地,它意味着“select”或“平行”,因为a <|> babab并行完成的“select”。 但是让我们回来吧。

真的,像(<*>)(<|>)这样的types类中的操作是没有实际意义的。 这些操作具有两种意义:(1)通过法律和(2)通过实例。 如果我们不是在讨论Alternative特定实例,那么只有(1)可用于直觉意义。

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

其他法律包括与empty身份。 特别a <|> empty = empty <|> a = a 。 在我们的“select”或“平行”的直觉中,这些定律被解读为“一个或(不可能的东西)必须是一个”或“一个(空的过程)只是一个”。 它表明empty是一种Alternative方式的某种“失败模式”。

(<|>) / emptyfmap (来自Functor )或pure / (<*>) (来自Applicative )进行交互的方式还有其他一些规律,但也许是理解(<|>)是研究实例化Alternative :a Parser一个非常常见的例子。

如果x :: Parser Ay :: Parser B那么(,) <$> x <*> y :: Parser (A, B) 依次parsingx y 。 相反, (fmap Left x) <|> (fmap Right y) x开始parsingxy ,以尝试两种可能的parsing。 换句话说,它表示分析树中的一个分支 ,一个select或一个并行分析的宇宙。

(<|>) :: fa -> fa -> fa实际上告诉了你很多,甚至不考虑Alternative的定律。

它需要两个fa值,并且必须返回一个值。 所以它必须以某种方式组合或select其input。 它在typesa是多态a ,所以它将完全无法检查fa可能存在的任何typesa值; 这意味着它不能通过组合a完成“组合”,所以它必须纯粹根据types构造函数f添加的任何结构。

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

现在,如果我有两个Maybe a值,我必须把它们结合起来,我该怎么办? 如果他们都Nothing我将不得不返回Nothing ,没有办法创builda 。 如果其中至less有一个是Just ...我可以按原样返回一个input,或者我可以返回Nothing 。 有很less的函数甚至可能的typesMaybe a -> Maybe a -> Maybe a ,而一个名为“另类”的类是给定的是非常合理和明显的。

如何结合两个[a]值? 这里有更多的可能的function,但是这很可能是非常明显的。 如果你熟悉列表monad / applicative的标准“非确定性”解释,那么“Alternative”这个名字给你提供了一个很好的暗示, 如果把一个[a]看作是a具有可能值的集合的“非确定性的a ”,那么以“可select”的名义来“合并两个不确定的a值”的明显方法是产生一个不确定的a可以是来自任何input的任何值。

对于parsing器 结合两个parsing器有两个明显的广泛的解释, 要么产生一个parsing器,它将匹配第一个和第二个parsing器,或者生成一个parsing器,它匹配第一个第二个解决scheme(每个这些选项都有细微的细节)可供select的空间)。 给定名称“替代”,“或”的解释对于<|>是非常自然的。

所以,从足够高的抽象层面来看,这些操作都是“做同样的事情”。 types类真的是在这样高的抽象层次上运行,这些东西都是“看起来相同”的。 当我在一个已知的实例上进行操作时,我只是将<|>操作看作是针对该特定types的操作。

另一个不是parsing器或类似MonadPlus的事情的一个有趣的例子是Concurrently ,它是async包中非常有用的一个types。

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

这些看起来很不一样,但请考虑:

 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可能只能保存一个值(或者是零,但不能是其他值)。 但是,嘿,如果它们都是相同的 ,为什么你需要两种不同的types? 他们的不同点是,你知道, 是不同的

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