Extracting nested monadic result: m (m a) -> m a

229 Views Asked by At

I have a function

parseArgs :: [String] -> StdGen -> IO ()

which selects the function to run. The main looks like

main = parseArgs <$> getArgs <*> getStdGen >>= id

The problem I have, parseArgs <$> getArgs <*> getStdGen is of type IO (IO ()), which I extract using (>>= id) which is of type Monad m => m (m b) -> m b. Is there a way to avoid requiring the "extraction" of the value while having just a single line function?

3

There are 3 best solutions below

5
On BEST ANSWER

The easiest way would be with join:

main = join $ parseArgs <$> getArgs <*> getStdGen

Personally, I would prefer the form

main = join $ liftM2 parseArgs getArgs getStdGen

where

join   :: Monad m => m (m a) -> m a
liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r

Or just use a do

main = do
    args <- getArgs
    gen  <- getStdGen
    parseArgs args gen
0
On

You could pair up the arguments and put them through a single bind:

main = uncurry parseArgs =<< (,) <$> getArgs <*> getStdGen

This avoids the extraction from nested IO. Admittedly it's no shorter but I find it easier to think about.

It fits a general pattern of doTheWork =<< getAllTheInputs which might be the way you'd end up arranging things anyway, if the code was more complicated.

0
On

You can define an operator for this:

infixl 4 <&>

(<&>) :: Monad m => m (a -> m b) -> m a -> m b
f <&> x = f >>= (x >>=)

If you have a function of type

f :: Monad m => (a1 -> a2 -> ... -> an -> m b) -> m a1 -> m a2 -> ... -> m an -> m b

then you can write

fx :: Monad m => m b
fx = f <$> x1 <*> x2 <*> ... <&> xn

where each xi has type m ai.

In your case it would be simply

parseArgs <$> getArgs <&> getStdGen