[英]What does Haskell's <|> operator do?
瀏覽 Haskell 的文檔對我來說總是有點痛苦,因為你獲得的關於函數的所有信息通常只是: fa -> f [a]
這可能意味着任何數量的事情。
與<|>
函數的情況一樣。
我得到的只是這個: (<|>) :: fa -> fa -> fa
並且它是一個“關聯二元運算” ......
在檢查Control.Applicative
我了解到它根據實現做了看似無關的事情。
instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
好的,所以如果沒有左,它返回右,否則它返回左,明白了..這讓我相信它是一個“左或右”運算符,考慮到它的使用|
和|
歷史上用作“OR”
instance Alternative [] where
empty = []
(<|>) = (++)
除了這里它只是調用列表的連接運算符......打破我的想法......
那么這個函數究竟是什么呢? 它有什么用? 它在宏偉的計划中處於什么位置?
通常它的意思是“選擇”或“並行”,因為a <|> b
是a
或b
的“選擇”,或者a
和b
並行完成。 但是讓我們備份。
實際上,像(<*>)
或(<|>)
這樣的類型類中的操作沒有實際意義。 這些操作的含義有兩種:(1)通過定律和(2)通過實例化。 如果我們不是在談論Alternative
的特定實例,那么只有 (1) 可用於直觀意義。
所以“關聯”意味着a <|> (b <|> c)
與(a <|> b) <|> c
。 這很有用,因為這意味着我們只關心用(<|>)
鏈接在一起的事物的順序,而不關心它們的“樹結構”。
其他法律包括身份與empty
。 特別是, a <|> empty = empty <|> a = a
。 在我們對“選擇”或“平行”的直覺中,這些定律讀作“a 或(不可能的東西)必須是a”或“a 旁邊(空過程)只是a”。 它表明empty
是Alternative
某種“失敗模式”。
還有其他關於(<|>)
/ empty
如何與fmap
(來自Functor
)或pure
/ (<*>)
(來自Applicative
)交互的fmap
,但也許是理解(<|>)
是檢查實例化Alternative
類型的一個非常常見的示例: Parser
。
如果x :: Parser A
和y :: Parser B
則(,) <$> x <*> y :: Parser (A, B)
依次解析x
和y
。 相比之下, (fmap Left x) <|> (fmap Right y)
解析無論是x
或y
,與始x
,嘗試兩種可能的解析。 換句話說,它表示您的分析樹中的一個分支、一個選擇或一個平行的分析領域。
(<|>) :: fa -> fa -> fa
實際上告訴你很多,即使不考慮Alternative
的法則。
它需要兩個fa
值,並且必須返回一個。 因此,它必須以某種方式組合或從其輸入中進行選擇。 它在a
類型中a
多態a
,因此它完全無法檢查fa
可能存在的任何類型a
值; 這意味着它不能通過組合a
值來進行“組合”,因此它必須純粹根據類型構造函數f
添加的任何結構來進行。
這個名字也有點幫助。 某種“或”確實是作者試圖用名稱“替代”和符號“<|>”表示的模糊概念。
現在,如果我有兩個Maybe a
值並且我必須將它們組合起來,我該怎么辦? 如果它們都是Nothing
我將不得不返回Nothing
,無法創建a
。 如果其中至少一個是Just ...
我可以按原樣返回我的輸入之一,或者我可以返回Nothing
。 很少有函數可以使用類型Maybe a -> Maybe a -> Maybe a
,並且對於名稱為“Alternative”的類,給出的函數非常合理且顯而易見。
結合兩個[a]
值怎么樣? 這里有更多可能的功能,但實際上很明顯這可能會做什么。 如果您熟悉列表 monad/applicative 的標准“非確定性”解釋,那么名稱“Alternative”確實可以很好地提示您這可能是什么; 如果您將[a]
視為具有可能值集合的“非確定性a
”,那么以一種可能應得“替代”名稱的方式“組合兩個非確定性a
值”的顯而易見的方法是產生一個非確定性a
可以是來自任一輸入的任何值。
對於解析器; 結合兩個解析器有兩個明顯的廣泛解釋; 要么你生成解析器會匹配第一個做什么,然后第二個做什么,或者你產生一種解析器,匹配要么什么第一呢還是什么,第二做(也有每個選項是假的當然微妙的細節選擇余地)。 鑒於名稱“替代”,“或”解釋對於<|>
似乎很自然。
所以,從一個足夠高的抽象層次看出,這些操作做“做同樣的事情。” 類型類實際上是為了在這些東西“看起來都一樣”的高抽象層次上操作。 當我在單個已知實例上操作時,我只是認為<|>
操作與它對特定類型所做的完全一樣。
一個不是解析器或類似 MonadPlus 的Alternative
的有趣示例是Concurrently
,它是async
包中非常有用的類型。
對於Concurrently
, empty
是一個永遠持續的計算。 And (<|>)
並發執行其參數,返回第一個完成的結果,並取消另一個。
這些看起來非常不同,但請考慮:
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]
A Maybe
只能容納一個值(或零,但不能容納任何其他數量)。 但是,嘿,如果它們都是相同的,為什么需要兩種不同的類型? 他們與眾不同的全部意義在於,你知道,要與眾不同。
總之,實現可能看起來完全不同,但它們實際上非常相似。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.