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 <|> b
是a
或b
或a
和b
并行完成的“select”。 但是让我们回来吧。
真的,像(<*>)
或(<|>)
这样的types类中的操作是没有实际意义的。 这些操作具有两种意义:(1)通过法律和(2)通过实例。 如果我们不是在讨论Alternative
的特定实例,那么只有(1)可用于直觉意义。
所以“联想”意味着a <|> (b <|> c)
与(a <|> b) <|> c
。 这是有用的,因为这意味着我们只关心与(<|>)
链接在一起的事物序列 ,而不是他们的“树结构”。
其他法律包括与empty
身份。 特别a <|> empty = empty <|> a = a
。 在我们的“select”或“平行”的直觉中,这些定律被解读为“一个或(不可能的东西)必须是一个”或“一个(空的过程)只是一个”。 它表明empty
是一种Alternative
方式的某种“失败模式”。
(<|>)
/ empty
与fmap
(来自Functor
)或pure
/ (<*>)
(来自Applicative
)进行交互的方式还有其他一些规律,但也许是理解(<|>)
是研究实例化Alternative
:a Parser
一个非常常见的例子。
如果x :: Parser A
和y :: Parser B
那么(,) <$> x <*> y :: Parser (A, B)
依次parsingx
和 y
。 相反, (fmap Left x) <|> (fmap Right y)
从 x
开始parsingx
或y
,以尝试两种可能的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。
Concurrently
, empty
是一个永远持续的计算。 和(<|>)
同时执行它的参数,返回第一个完成的结果,并取消另一个。
这些看起来很不一样,但请考虑:
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? 他们的不同点是,你知道, 是不同的 。
总之, 实现可能看起来完全不同,但实际上非常相似。