I'm new to Haskell and FP so this question may seem silly.
I have a line of code in my main function
let y = map readFile directoryContents
where directoryContents is of type [FilePath]
. This in turn (I think) makes y type [IO String]
, so a list of strings - each string containing the contents of each file in directoryContents
.
I have a functions written in another module that work on [String]
and String
but I'm unclear as how to call/use them now because y is of type [IO String]
. Any pointers?
EDIT:
It was suggested to me that I want to use mapM
instead of map
, so:
let y = mapM readFile directoryContents
, and y
is now type IO [String]
, what do I do from here?
You're correct, the type is y :: [IO String]
.
Well, there are essentially main two parts here:
[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.
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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With