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

  • A+
Category:Languages

Type signature of Haskell's bind operator (>>=):

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

Type signature of F#'s forward pipe operator (|>):

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

They look similar. And considering the impure nature of F#, the equivalent operator of |> in Haskell is >>=?

For example:

Haskell:

getLine >>= putStrLn 

F#:

stdin.ReadLine() |> stdout.Write 

 


Not really. If you specialize m to IO, then there are some superficial similarities, so maybe it’s true that (>>=) @IO is sort of like F#’s |>, but in general, the similarity does not hold.

If we specialize m to Maybe, then >>= is like Option.bind, just with the arguments flipped (which makes sense, since >>= is pronounced “bind”).

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

If we specialize m to Either e, then >>= does something similar to what it does for Maybe, short-circuiting on Left values instead of Nothing. These examples are sort of similar to using |> with functions that raise exceptions, but they aren’t quite the same.

If we specialize m to Parser (from, say, the megaparsec package), then >>= produces a new parser that runs a first parser, then uses its result to determine which parser to run next. For example, this defines a parser that produces a parser that parses two digits or a non-digit followed by an arbitrary character:

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

This is rather different from |>, since we’re not running anything, merely building up a structure (a parser) that will be applied to a value later, yet the code still talks about the value that will eventually be provided (in the c binding).

If we specialize m to (->) r, then >>= implements a kind of implicit argument passing. For example, if we have a set of functions that all accept a common argument:

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

…then we can use >>= to compose them together, passing the same first argument to all of them:

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

This is clearly different from |>, since we’re performing a sort of function composition, not function application.

I could go on, but listing dozens of examples probably isn’t any more helpful than just listing a few. The takeaway is that >>= is not just for sequencing effectful things, it is a much more general abstraction of which sequencing IO actions is a special case. The IO case is pragmatically useful, of course, but it’s also probably the least theoretically interesting, since it’s a little bit magical (IO is baked into the runtime). These other uses of >>= are not magical in the slightest; they are defined entirely using ordinary, pure Haskell code, but they’re still very useful, so they’re much more relevant to understanding the essence of >>= and Monad than IO is.


As a final aside, Haskell does have a function just like F#’s |>. It is called &, and it comes from the Data.Function module. It has the same type that it does in F#:

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

This function is quite useful in its own right, but it has nothing to do with monads.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: