How to flatten IO (IO ())?

  • A+
Category:Languages

I'm just learning Haskell and monad transformers and I've found myself with an IO (IO ()) that I'd like to flatten into just IO (). I'm sure that I'm doing something wrong, but can't pinpoint exactly where I'm getting lost.

Here is a simplified example of what I'm trying to do. This is a convoluted way of implementing echo, but it illustrates the problem.

userInput :: Monad m => ReaderT (IO String) m (IO String) userInput = ask  echo :: Monad m => ReaderT (IO String) m (IO ()) echo = userInput >>= /input ->  -- unwrap ReaderT to get an IO String          input >>= (/s ->       -- unwrap IO String to get a String            putStrLn s)          -- print out the String          & return               -- rewrap into a ReaderT  main :: IO (IO ())              -- How to turn IO (IO ()) to IO ()? main = runReaderT echo getLine 

In my real application, I have a Spock app that makes HTTP requests to an upstream server. Spock apps use a monad transformer stack called SpockCtxT and I'd like to insert a ReaderT into the stack to abstract the HTTP request so that I can swap it out for a mock implementation in my tests.

Fundamentally, the idea is a monad transformer stack where one of the transformers gives you an IO whether it be an HTTP request or getLine. Am I thinking about this incorrectly or is there some way to do this?

 


Use join. It has the type signature

join :: Monad m => m (m a) -> m a 

which specializes to

join :: IO (IO ()) -> IO () 

you can use hoogle to find this out. It is a command-line tool. We can search by type signature:

hoogle "IO (IO ()) -> IO ()" 

gives

Control.Monad join :: Monad m => m (m a) -> m a Control.Composition (.$) :: Monad m => m (m a) -> m a RIO join :: Monad m => m (m a) -> m a Universum.Monad.Reexport join :: Monad m => m (m a) -> m a Stack.Prelude join :: Monad m => m (m a) -> m a Relude.Monad.Reexport join :: Monad m => m (m a) -> m a Intro join :: Monad m => m (m a) -> m a Hledger.Web.Import join :: Monad m => m (m a) -> m a Data.Edison.Seq concat :: Sequence s => s (s a) -> s a Data.Edison.Seq.Defaults concatUsingFoldr :: Sequence s => s (s a) -> s a -- plus more results not shown, pass --count=20 to see more 

which has several functions that are exactly what you want.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: