代码之家  ›  专栏  ›  技术社区  ›  weakish

Haskell的bind运算符(>>=)是否等同于F的forward pipe运算符(|>)?

  •  3
  • weakish  · 技术社区  · 6 年前

    Haskell的绑定运算符的类型签名(>gt;=):

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

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

    它们看起来很相似。 考虑到F#的不纯性质, 等价算子 |> 在哈斯克尔 >>= ?

    例如:

    getLine >>= putStrLn
    

    F#:

    stdin.ReadLine() |> stdout.Write
    
    2 回复  |  直到 6 年前
        1
  •  9
  •   Alexis King    6 年前

    不是真的。如果你专攻 m IO ,那么有一些表面上的相似之处,所以也许这是真的 (>>=) @IO 有点像F |> 但总的来说,这种相似性并不成立。

    Maybe ,然后 >>= 就像 Option.bind >>= 发音为bind)。

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

    如果我们专攻 Either e ,然后 >> 做一些类似的事情 也许 吧 Left 值而不是 Nothing |> 有引发异常的函数,但它们并不完全相同。

    如果我们专攻 Parser (比如说 megaparsec 包装),然后 >>

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

    这与 |> c 具有约束力)。

    如果我们专攻 (->) r ,然后 >>= 实现一种隐式参数传递。例如,如果我们有一组函数都接受一个公共参数:

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

    >>=

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

    这是 清晰地 |> ,因为正在执行某种函数组合,而不是函数应用程序。

    >>= 输入输出 行动是一个特例。这个 当然,case在实用上是有用的,但在理论上可能也是最不有趣的,因为它有点神奇( 输入输出 >>= 一点也不神奇;它们完全是使用普通的、纯Haskell代码定义的,但是它们仍然非常有用,因此它们与理解 >> Monad 输入输出


    |> . 它被称为 & ,它来自 Data.Function

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

    这个函数本身就很有用,但它与monad无关。

        2
  •  8
  •   Mark Seemann    6 年前

    虽然F#不区分纯操作和非纯操作,但它确实有单子的概念。当你与 computation expressions . 为了实现计算表达式,必须实现 一元结合 M<'T> * ('T -> M<'U>) -> M<'U> ,尽管这是伪代码,因为 M<'T>

    F#带有一些内置的monad,例如 Async<'a> , 'a list , 'a seq . 您还可以为 'a option Result ,尽管我不认为这些都是内置的。

    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 值,则必须使用一元绑定:

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

    |> 运算符也存在于Haskell中,但您必须导入 Data.Function

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