1

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?

bheklilr
  • 53,530
  • 6
  • 107
  • 163
seequ
  • 456
  • 2
  • 13
  • 1
    Don't you already have a single line function ? – Sibi Nov 25 '14 at 18:50
  • I'm looking for a way to avoid the extraction, which is done by the `(>>= id)` function. – seequ Nov 25 '14 at 18:51
  • May I ask why you want to avoid using `(>>= id)`? It seems the best way if you're using a `Monad m => m (m a) -> m a` constraint, because some other solutions might have `(Applicative m, Monad m)` etc, which makes less sense in the context. – AJF Dec 02 '14 at 19:16
  • @AJFarmar I wasn't avoiding particularly `(>>= id)`, but any requirement to do the extraction whatsoever. Seems like that is impossible. Not that it really matters, because all this was purely for learning purposes. – seequ Dec 02 '14 at 19:47

3 Answers3

4

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
bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • Well, apparently `join` is exactly the same I was doing. It still doesn't answer the question though. Is it possible or not? – seequ Nov 25 '14 at 18:53
  • @Seig The question was "Is there a way to avoid requiring the 'extraction' (`>>= id`) of the value while having just a single line function?" The answer to that question is yes, using `join`. Are you wanting an existing combinator that would have the type `Monad m => (a -> b -> m r) -> m a -> m b -> m r`? A quick search on Hoogle doesn't come up with anything, I'd say that `join` would be the idiomatic way to do this. – bheklilr Nov 25 '14 at 18:58
  • Well, that fulfills it. – seequ Nov 25 '14 at 18:59
  • 1
    `main = join $ liftM2 parseArgs getArgs getStdGen` how can you prefer this?!? – alternative Nov 25 '14 at 21:03
  • @alternative It only takes 1 import, and uses fewer operators and symbols. Really, I'd prefer do notation, since it's abundantly obvious what's happening, but if given the choice between the first two, I'd stick with the entirely monadic one, not the monadic and applicative one. But it's all opinion, I certainly don't regard the first as "wrong" in any way. – bheklilr Nov 25 '14 at 21:06
3

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
effectfully
  • 12,325
  • 2
  • 17
  • 40
2

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.

David Fletcher
  • 2,590
  • 1
  • 12
  • 14