17

I have two functions, one that tries to get a token from a webservice and may fail, and one that tries to use this token to get the username and may fail.

getToken :: IO (Maybe Token)
getUsername :: Token -> IO (Maybe String)

I would like to take the result of getToken and feed it to getUsername. If there was only IO or Maybe, I could simply use bind, but since there are down nested monads, I can't. How can I write something equivalent to getToken >>= getUsername :: IO (Maybe String) ?

More generally, what function has type m1 m2 a -> (a -> m1 m2 b) -> m1 m2 b?

Bonus question: how would I do that using the do notation in an IO context?

madjar
  • 12,691
  • 2
  • 44
  • 52
  • 2
    The general way to combine monads is to use monad transformers. In your case, `MaybeT` (http://hackage.haskell.org/package/transformers-0.4.2.0/docs/Control-Monad-Trans-Maybe.html#v:MaybeT) – Anton Guryanov Jan 29 '15 at 12:49
  • MaybeT is indeed the best solution here. – Bartek Banachewicz Jan 29 '15 at 12:49
  • 1
    There is not, in general, a function of type `(Monad m1, Monad m2) => m1 (m2 a) -> (a -> m1 (m2 b)) -> m1 (m2 b)`. This is what is meant when people say "monads don't compose". See also [Concrete example showing that monads are not closed under composition](http://stackoverflow.com/q/13034229/791604). – Daniel Wagner Jan 29 '15 at 17:22

2 Answers2

16

I have defined a function useToken showing your use case:

type Token = String

getToken :: IO (Maybe Token)
getToken = undefined

getUsername :: Token -> IO (Maybe String)
getUsername = undefined

useToken :: IO (Maybe String)
useToken = do
  token <- getToken
  case token of
    Just x -> getUsername x
    Nothing -> return Nothing

If you don't want to use do notation, then you can use:

useToken2 :: IO (Maybe String)
useToken2 = getToken >>= \token -> maybe (return Nothing) getUsername token

Or using monad transformers, your code will become simpler:

import Control.Monad.Trans.Maybe
type Token = String

getToken :: MaybeT IO Token
getToken = undefined

getUsername :: Token -> MaybeT IO String
getUsername = undefined

useToken :: MaybeT IO String 
useToken = do
  token <- getToken
  getUsername token

Note that, you can also directly lift IO operations inside the monad transformer. As @Robedino points out, now the code will be more concise without do notation:

useToken :: MaybeT IO String 
useToken = getToken >>= getUsername
Sibi
  • 47,472
  • 16
  • 95
  • 163
  • That is indeed the code I have written to do that. Do you think there is a more general solution to that? Or should I be satisfied with that formulation? – madjar Jan 29 '15 at 12:44
  • @madjar I have added a code without do notation. I don't know if there is more general solution than this. – Sibi Jan 29 '15 at 12:48
  • And, instead of using do-notation in useToken, you can also use: ```getToken >>= getUsername``` with the ```MaybeT``` solution! – RobertDiep Jan 29 '15 at 13:30
  • @Robedino Oh yes, that would be more sweeter! – Sibi Jan 29 '15 at 13:31
  • Also, in order to run ```useToken``` now you should use ```runMaybeT :: MaybeT m a -> m (Maybe a)```. ```runMaybeT useToken``` now has type ```IO (Maybe String)```, instead of ```MaybeT IO String``` :) – RobertDiep Jan 29 '15 at 13:42
7

As people in the comments suggest, you should just use monad transformers.

However you can avoid this in your case. Monads do not commute in general, so you can't write a function with this signature

bind' :: (Monad m, Monad n) => m (n a) -> (a -> m (n b)) -> m (n b)

But all is ok, if the inner monad is an instance of the Traversable class:

import Data.Traversable as T
import Control.Monad

joinT :: (Monad m, Traversable t, Monad t) => m (t (m (t a))) -> m (t a)
joinT = (>>= liftM join . T.sequence)

liftMM :: (Monad m, Monad n) => (a -> b) -> m (n a) -> m (n b)
liftMM = liftM . liftM

bindT :: (Monad m, Traversable t, Monad t) => m (t a) -> (a -> m (t b)) -> m (t b)
bindT x f = joinT (liftMM f x)

and the Maybe monad is; hence

type Token = String

getToken :: IO (Maybe Token)
getToken = undefined

getUsername :: Token -> IO (Maybe String)
getUsername = undefined

useToken :: IO (Maybe String)
useToken = getToken `bindT` getUsername

Also, with the {-# LANGUAGE RebindableSyntax #-} you can write

(>>=) = bindT

useToken :: IO (Maybe String)
useToken = do
    x <- getToken
    getUsername x

Update

With the type-level compose

newtype (f :. g) a = Nested { runNested :: f (g a) }

you can define a monad instance for nested monads:

instance (Monad m, Traversable t, Monad t) => Monad (m :. t) where
    return  = Nested . return . return
    x >>= f = Nested $ runNested x `bindT` (runNested . f)

Your example then is

type Token = String

getToken :: IO (Maybe Token)
getToken = undefined

getUsername :: Token -> IO (Maybe String)
getUsername = undefined

useToken :: IO (Maybe String)
useToken = runNested $ Nested getToken >>= Nested . getUsername

Or like you would do with the MaybeT transformer:

type Nested = (:.)

type Token = String

getToken :: Nested IO Maybe Token
getToken = undefined

getUsername :: Token -> Nested IO Maybe String
getUsername = undefined

useToken :: Nested IO Maybe String
useToken = getToken >>= getUsername

runUseToken :: IO (Maybe String)
runUseToken = runNested useToken
effectfully
  • 12,325
  • 2
  • 17
  • 40