[英]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
並不是的。 如果你擅長m
至IO
,然后有一些表面上的相似,也許這是真的, (>>=) @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 option
和Result
創建計算表達式,雖然我不認為其中任何一個是內置的。
您可以仔細閱讀各種計算表達式構建器的源代碼,以確定如何為每個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.