Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 922
weakish Avatar asked Aug 12 '18 13:08

weakish


1 Answers

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.

like image 57
Alexis King Avatar answered Nov 15 '22 07:11

Alexis King