I'm new to haskell and functional programming in general and I have a problem with monads. Let's say I have got a list of filenames:
-- do block --
let filenames = ["file1","file2"]
and I want to generate a list of those files' content, using list comprehensions:
let content = [str <- readFile f | f <- filenames]
Ofc, this kind of usage is not valid. As I understand this kind of "assignment" can be used in do block, when "chaining" the result with the next instruction.
Is there maybe an alternative way to use the left arrow (or >>=) operator. I imagine sth like this:
let content = [leftArrAlter $ readFile f | f <- filenames]
Let's start with the simpler list
let content = [readFile f | f <- filenames]
content has type [IO String]; it's a list of IO actions, each of which can produce a String when executed.
What you would like is something of type IO [String]: a single IO action that, when executed, gives you a list of String values.
That's where the sequence function comes in. In this case, we only need to consider the specialized version that has type [IO String] -> IO [String]:
content <- sequence [readFile f | f <- filenames]
We can also use traverse, in particular the specialized version with type (FilePath -> IO String) -> [FilePath] -> IO [String]:
content <- traverse readFile fileNames
For reference, the general types of the two functions:
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
We used [] as our Traversable, and IO as our Monad/Applicative.
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