簡體   English   中英

Haskell的綁定運算符(>> =)是否等同於F#的正向管道運算符(|>)?

[英]Is Haskell's bind operator (>>=) equivalent to F#'s forward pipe operator (|>)?

輸入Haskell綁定運算符的簽名(>> =):

m a -> (a -> m b) -> m b

輸入F#的前向管道運算符(|>)的簽名:

'a -> ('a -> 'b) -> 'b

他們看起來很相似 考慮到F#的不純性,Haskell中的|>的等價運算符是>>=

例如:

哈斯克爾:

getLine >>= putStrLn

F#:

stdin.ReadLine() |> stdout.Write

並不是的。 如果你擅長mIO ,然后有一些表面上的相似,也許這是真的, (>>=) @IO 有點像F#的|>但在一般情況下,類似不成立。

如果我們將m專門化為Maybe ,則>>=就像Option.bind一樣,只需翻轉參數(這是有道理的,因為>>=發音為“bind”)。

ghci> Just [1, 2, 3] >>= headMay
Just 1
ghci> Just [] >>= headMay
Nothing
ghci> Nothing >>= headMay
Nothing

如果我們將m專門用於Either e ,那么>>=做類似於它對Maybe ,在Left值上短路而不是Nothing 這些示例類似於使用|>函數來引發異常,但它們並不完全相同。

如果我們將m專門用於Parser (例如,來自megaparsec包),那么>>=生成一個運行第一個解析器的新解析器,然后使用其結果來確定下一個運行哪個解析器。 例如,這定義了一個解析器,它生成一個解析器,解析兩個數字或一個非數字后跟一個任意字符:

p :: Parser Char
p = anyChar >>= \c -> if isDigit c then digit else anyChar

這與|>不同,因為我們沒有運行任何東西,只是構建一個稍后將應用於某個值的結構(解析器),但代碼仍然會討論最終將提供的值(在c綁定)。

如果我們將m專門化為(->) r ,則>>=實現一種隱式參數傳遞。 例如,如果我們有一組函數都接受一個共同的參數:

f :: Key -> String
g :: String -> Key -> Char
h :: Char -> Key -> Bool

...然后我們可以使用>>=將它們組合在一起,將相同的第一個參數傳遞給所有它們:

ghci> :t f >>= g >>= h
f >>= g >>= h :: Key -> Bool

明顯不同於|> ,因為我們正在執行一種功能組合,而不是功能應用。

我可以繼續,但列出幾十個例子可能不僅僅是列出一些例子。 需要注意的是, >>=不僅僅是對有效的事物進行排序,它是一種更為通用的抽象,其中排序IO動作是一種特殊情況。 當然, IO案例在實用上是有用的,但它在理論上也可能是最不感興趣的,因為它有點神奇( IO已經融入運行時)。 >>=其他用法絲毫不是神奇的; 它們完全使用普通的純Haskell代碼定義,但它們仍然非常有用,因此它們與理解>>=Monad的本質比IO更為相關。


最后一點,Haskell 確實有一個函數就像F#的|> 它被稱為& ,它來自Data.Function模塊。 它與F#中的類型相同:

(&) :: a -> (a -> b) -> b

這個函數本身非常有用,但它與monad無關。

雖然F#不區分純操作和不純操作,但它確實具有monad的概念。 使用計算表達式時,這是最明顯的。 要實現計算表達式,必須實現monadic綁定 在F#文檔中,它必須具有類型M<'T> * ('T -> M<'U>) -> M<'U> ,盡管這是偽代碼,因為像M<'T>這樣的類型M<'T>不是正確的F#語法。

F#帶有一些內置的monad,例如Async<'a>'a list 'a seq 你也可以簡單地為'a optionResult創建計算表達式,雖然我不認為其中任何一個是內置的。

您可以仔細閱讀各種計算表達式構建器的源代碼,以確定如何為每個AJFarmar實現AJFarmar綁定,但AJFarmar是正確的,它們通常稱為collect

> List.collect;;
val it : (('a -> 'b list) -> 'a list -> 'b list)

> Array.collect;;
val it : (('a -> 'b []) -> 'a [] -> 'b [])

> Seq.collect;;
val it : (('a -> #seq<'c>) -> seq<'a> -> seq<'c>)

但並不總是如此。 有時該操作稱為bind

> Option.bind;;
val it : (('a -> 'b option) -> 'a option -> 'b option)

為了說明,請考慮使用這個小F#helper函數將字符串解析為整數:

open System

let tryParse s =
    match Int32.TryParse s with
    | true, i -> Some i
    | _ -> None

如果你有一個字符串,你可以使用正向管道:

> "42" |> tryParse;;
val it : int option = Some 42

另一方面,如果您的字符串已經是option值,則必須使用monadic綁定:

> Some "42" |> Option.bind tryParse;;
val it : int option = Some 42

|>運算符也存在於Haskell中,但您必須導入Data.Function

Prelude Data.Function> :t (&)
(&) :: a -> (a -> b) -> b

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM