-1

In Haskell Monad is declared as

class   Applicative m   =>  Monad   m   where
    return  ::  a   ->  m   a
    (>>=)   ::  m   a   ->  (a  ->  m   b)  ->  m   b
    return  =   pure

I was wondering if it is okay to redeclare the bind operator as

(>>=)   ::  (a  ->  m   b)  ->  m   a   ->  m   b

?

Is it correct that the second declaration makes it clearer that (>>=) maps a function of type a -> m b to a function of type m a -> m b, while the original declaration makes less clear what it means?

Will that change of declaration make something from possible to impossible, or just require some change of using monad (which seems bearable to Haskell programmers)?

Thanks.

Tim
  • 1
  • 141
  • 372
  • 590
  • 6
    No that is the flipped version. This is implemented as `(=<<)`. – Willem Van Onsem Jul 21 '19 at 15:57
  • Thanks. What do you "no" to? Is it correct that the second declaration makes it clearer that (>>=) maps a function of type a -> m b to a function of type m a -> m b, while the original declaration makes less clear what it means? Will that change of declaration make something from possible to impossibile, or just require some change of using monad (which seems bearable to Haskell programmers)? – Tim Jul 21 '19 at 16:08
  • 4
    This is actually an interesting topic to discuss - the fact that the type signature for `>>=` is, as you note, "backwards" compared to its close counterparts `fmap` and `<*>`. I think @leftaroundabout's answer gives a good answer to why Haskell chose to define `>>=` with the arguments in that order - but ultimately it's fairly arbitrary and a matter of opinion which way would be better. And it's not clear to me exactly what your question is. – Robin Zigmond Jul 21 '19 at 16:15
  • 2
    The declared order is meant to facilitate left-to-right chaining of Kleisli arrows, rather than suggest any particular mapping. Given two such arrows `f :: a -> m b` and `g :: b -> m c`, you can write either `h x = x >>= f >>= g`. The left-to-right "composition" was deemed more natural given the use of monads to represent sequential computation. – chepner Jul 21 '19 at 16:51
  • cf. [this](https://stackoverflow.com/a/11249714/849891) (and also, [2](https://stackoverflow.com/a/34561605/849891), [3](https://stackoverflow.com/a/54962626/849891), [4](https://stackoverflow.com/a/53829742/849891)). `(.)` composes regular functions, `<=<` composes Kleisli arrows: `(f <=< g) = (f =<<) . (g =<<) . return`. – Will Ness Jul 21 '19 at 17:23
  • 1
    to answer your second question explicitly: it is just `flip (>>=)` which is also defined as `(=<<)`, so no, it does not make anything impossible or possible which wasn't before. `flip f y x = f x y`, it's all. – Will Ness Jul 22 '19 at 06:44

1 Answers1

14

There's one reason why >>= tends to be more useful in practice than it's flipped counterpart =<<: it plays nicely with lambda notation. Namely, \ acts as a syntactic herald, so you can continue the computation without needing any parentheses. For instance,

 do x <- [1..5]
    y <- [10..20]
    return $ x*y

can be rewritten very easily in terms of >>= as

   [1..5] >>= \x -> [10..20] >>= \y -> return $ x*y

You still have much the same “imperative flow” feel as with the do version.

Whereas with =<< it would require awkward parentheses and seem to read backwards:

   (\x -> (\y -> return $ x*y) =<< [10..20]) =<< [1..5]

Ok, you might say this feels more like function application. But where that is useful, it is often more poignant to use only the applicative functor interface rather than the monadic one:

  (\x y -> x*y) <$> [1..5] <*> [10..20]

or short

  (*) <$> [1..5] <*> [10..20]

Note that (<*>) :: f (a->b) -> f a -> f b has essentially the order of =<< that you propose, just with the a-> inside the functor rather than outside.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • Thanks. Is it correct that my second declaration makes it clearer that (>>=) maps a function of type a -> m b to a function of type m a -> m b, while the original declaration makes less clear what it means? – Tim Jul 21 '19 at 16:13
  • 1
    I don't know. Perhaps. – leftaroundabout Jul 21 '19 at 16:15
  • 8
    It doesn't make it clearer; it makes it *true*. The original declaration *doesn't* map a function of type `a -> m b` to a function of type `m a -> m b`; it maps a value of type `m a` to a function of type `(a -> m b) -> m b`. But the purpose of `>>=` isn't to map things so much as to "link" computations; the output of `x >>= f` is a value suitable for the left argument to another call to `>>=`: `x >>= f >>= g >>= ...` – chepner Jul 21 '19 at 16:28