3

I can't understand the difference between Dot (function composition) and bind (>>=) .

If I understand, these two ways take the previous result of a function for a new function.

So what is the difference ?

Guillaume
  • 191
  • 10
  • 4
    `(.)` is an operator which works with function types whereas `(>>=)` is an overloaded operator. It works with all types which are instances of Monad type class including the function type `(->) r`. Even with function type they are not exactly the same. However I would advise you to check `fmap` and `(.)` first. – Redu Nov 21 '19 at 17:27
  • 1
    Roughly put, `(.)` composes `a -> b` and `b -> c`, while `>>=` sort-of _applies_ `M b` to `b -> M c`. Types are completely different, especially because of the monad `M`. – chi Nov 21 '19 at 17:48
  • 3
    On the other hand `(.)` and `(<=<)` ("kleisli composition") are quite similar and the difference/analogy between them is worth thinking about – luqui Nov 21 '19 at 18:15
  • @luqui indeed, and the `Category` class unifies this general concept of "things that can be composed". (And `Arrow` and its subclasses take it further.) (Note I'm mentioning these in case the OP is interested, not because I think you're unaware of them :-) ) – Robin Zigmond Nov 21 '19 at 19:19

2 Answers2

5

They are pretty different. Let's look at their signatures:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

As you said, the function composition is just a way to pass a result of one function as an argument to another one like this:

f = g . h

is equivalent to

f x = g (h x)

You can think about is as some kind of a "conveyor", where your value goes through several processing steps.

But (>>=) is quite different. It is related to such context as monad which is something like some value in some context (it's highly recommended to read the previous link if you aren't familiar with it).

So let x be some value in a context. Our context will be nullability (Maybe monad), and the value is 2. So, x = Just 2. We could, for example, get it as a result of a lookup from some associative container (such operation might fail, that's the reason why it is Maybe Int, but not Int).

Now we want to pass our x to some arithmetic function f that accepts just Int and may fail, so its signature looks like:

f :: Int -> Maybe Int

We can't just pass our value because of type mismatch. We could unpack x and handle some cases with if, but we could do that in almost all other languages. In haskell, we can use (>>=):

x >>= f

This allows as to chain the effects:

  • if x is Nothing, then the result is Nothing immediately
  • else x is unpacked and passed to f

This is a generalization of the operator ?., that you could see in some languages:

x = a?.func1()?.func2();

which checks for null at each "step" and stops immediately if hits null or returns the value in case of success. In haskell it looks like:

x = a >>= func1 >>= func2

However, bind with monads is a much more powerful concept, allowing you, for example, to emulate stateful computations in a language without mutability like haskell.

pat
  • 12,587
  • 1
  • 23
  • 52
Yuri Kovalenko
  • 1,325
  • 9
  • 19
  • Bind with monads is a much more powerful concept, allowing you, for example, to implement backtracking, coroutines, continuations, exceptions, undoable actions, transactional memory. Mutable state is the most boring example ever. – luqui Nov 22 '19 at 07:17
  • @luqui Whoa. I'm not so experienced in Haskell, so didn't know that monads are that powerful. – Yuri Kovalenko Nov 22 '19 at 09:50
4

(>>=) is a form of function application.

(>>=)          :: Monad m => m a -> (a -> m b) -> m b
flip ($)       ::              a -> (a ->   b) ->   b

It takes a value, but "extracts" part of it in order to apply the given function. Chaining two functions, like x >>= f >>= g requires the argument type of g to be different from (but at the same type similar to) the return type of f, unlike composition, which requires the types to match exactly.

Composed with return, it really is just function application, but restricted to certain kinds of functions.

flip ($)       ::              a -> (a ->   b) ->   b
(>>=) . return :: Monad m =>   a -> (a -> m b) -> m b

(.) is more like (<=<) (from Control.Monad).

(.)   ::            (b ->   c) -> (a ->   b) -> a ->   c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c

But again, instead of simply passing the result of one function to another, it first "extracts" a value before doing application.

chepner
  • 497,756
  • 71
  • 530
  • 681