7

just looking for an explanation how does following composition work:

(=<<) . return

where

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

The final type:

GHCi> :t (=<<) . return
(=<<) . return :: Monad m => m b -> m a -> m b

I cann't grasp how can match m a with (a -> m b) , ie. how can apply result of return which is a simple type to first argument of (=<<) expecting a function type ?

Petr
  • 62,528
  • 13
  • 153
  • 317
David Unric
  • 7,421
  • 1
  • 37
  • 65
  • 1
    `(=<<) . return :: (Monad ((->) a), Monad m) => m b -> m a -> m b` is a more complete type signature, which shows explicitly the function instance. – Sarah Aug 19 '12 at 08:41
  • Thanks. Unfortunately this extra constraint didn't shed any light to better understanding. Let pass Int to return and Monad m is of Maybe type, so the result is Maybe Int. If applied to (=<<) I get an error *GHCi> :t (=<<) (Just 1) Couldn't match expected type `a0 -> m0 b0' with actual type `Maybe a1'* – David Unric Aug 19 '12 at 09:03
  • as we've seen by now, `return` is a synonym for `const` here. Its meaning is fixed by its belonging to the ((->) a) monad. **`(=<<) . return === flip (>>)`**. So both uses of `Just` and `1` in your comment above were wrong. Instead of `Just` is must be only `const`, and instead of `1` it must be some monadic value, e.g. `(=<<) (const [1]) "xyz"` produces `[1,1,1]`. – Will Ness Aug 20 '12 at 11:43
  • Right, I already know. I did get it after reading Pudlak's answer. – David Unric Aug 20 '12 at 18:40
  • @DavidUnric great! :) Just wanted to put it down "on paper" explicitly. – Will Ness Aug 21 '12 at 06:50

2 Answers2

7

The explanation is:

  1. Your return is (perhaps unexpectedly) of a different monad than your =<<.
  2. The monad it uses is the reader monad (->) r.

The compiler tries to unify the result of return :: c -> m' c with the first argument of (a -> m b) -> m a -> m b, so to unify m' c with a -> m b. The only possibility is that m' is the reader monad (->) r for some r. Next, it tries to unify (->) r c with (converted to the prefix notation) (->) a (m b), which is solved by setting r to a and c to m b. So, after unification, the compiler gets the most general possible type

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

or in the usual infix notation

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

See also:


Edit: To define a monad, we need a (partially applied) type of kind * -> *. These are almost always partially applied data constructors, but this particular case, we view -> as a type operator that takes 2 type arguments and creates a new type (the function type). So for any given type r, the partially applied expression (->) r is a type of kind * -> *. As it turns out, there is a straightforward way how to describe monad operations on it. See Control.Reader monad and also this article that explains it. The monad operations for Reader are implemented exactly the same way as for (->), the only difference is that Reader wraps the operations into a distinct data type.

Community
  • 1
  • 1
Petr
  • 62,528
  • 13
  • 153
  • 317
  • Thank you. Just didn't realized the reader monad has to be applied to fullfil the type unification. So the remaining question is about distinguishing between function type, type constructor and "reader monad". I guess reader monad is a special case of type constructor and function type is equivalent to type constructor but I may be wrong. – David Unric Aug 19 '12 at 17:49
  • 1
    @DavidUnric: Well, a function arrow (`->`) is in fact a type constructor (of a kind `* -> * -> *`). You can of course partially apply it to some type `r` and get `(->) r :: * -> *`, which unifies with `m` just fine. Reader monad is nothing less than a monad instance over this partially applied function arrow. – Vitus Aug 19 '12 at 21:41
1

Me again, with the scribblings, putting stuff side-by-side to aid in visual comprehension:

g = ((=<<) . return)   {-

((=<<) . return) x y 
         ===   (=<<)    (return x)    y               return :: a' -> m' a'
               (=<<) :: (a -> m b) -> m a -> m b      return x :: m' a' , x :: a'
                          m'  a'                      m' ~ ((->) a) , a' ~ m b 

return x === const x             -- instance Monad ((->) a) where return = const
g x y === y >>= (\_ -> x) === y >> x   (!!)
-}

g :: m b -> m a -> m b

So as it turns out (and was perhaps apparent from the type signature), g === flip (>>):

Prelude> ((=<<).return) [1] "xyz"   -- ===  (=<<) (const [1]) "xyz" 
                                    -- ===  "xyz" >>= const [1]
                                    -- ===  "xyz" >> [1]
                                    -- ===  (>> [1]) "xyz"
[1,1,1]
Will Ness
  • 70,110
  • 9
  • 98
  • 181