[英]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.