You're correct, the type is y :: [IO String]
.
Well, there are essentially main two parts here:
How to turn [IO String] into IO [String]
[IO String]
is a list of of IO
actions and what we need is an IO action that carries a list of strings (that is, IO [String]
). Luckily, the function sequence provides exactly what we need:
sequence :: Monad m => [m a] -> m [a]
y' = sequence y :: IO [String]
Now the mapM
function can simplify this, and we can rewrite y'
as:
y' = mapM readFile directoryContents
mapM
does the sequence for us.
How to get at the [String]
Our type is now IO [String]
, so the question is now "How do we get the [String] out of the IO?" This is what the function >>=
(bind) does:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
-- Specialized to IO, that type is:
(>>=) :: IO a -> (a -> IO b) -> IO b
We also have a function return :: Monad m => a -> m a
which can put a value "into" IO
.
So with these two functions, if we have some function f :: [String] -> SomeType
, we can write:
ourResult = y' >>= (\theStringList -> return (f theStringList)) :: IO SomeType
Functions can be "chained" together with the >>=
function. This can be a bit unreadable at times, so Haskell provides do
notation to make things visually simpler:
ourResult = do
theStringList <- y'
return $ f theStringList
The compiler internally turns this into y' >>= (\theStringList -> f theStringList)
, which is the same as the y' >>= f
that we had before.
Putting it all together
We probably don't actually want y'
floating around, so we can eliminate that and arrive at:
ourResult = do
theStringList <- mapM readFile directoryContents
return $ f theStringList
Even more simplification
It turns out, this doesn't actually need the full power of >>=
. In fact, all we need is fmap
! This is because the function f
only has one argument "inside" of IO
and we aren't using any other previous IO
result: we're making a result then immediately using it.
Using the law
fmap f xs == xs >>= return . f
we can rewrite the >>=
code to use fmap like this:
ourResult = fmap f (mapM readFile directoryContents)
If we want to be even more terse, there is an infix synonym for fmap
called <$>
:
ourResult = f <$> mapM readFile directoryContents