The lines function in Haskell separates the lines of a string into a string list:
lines :: String -> [String]
The readFile function reads a file into a string:
readFile :: FilePath -> IO String
Trying to compose these functions to get a list of lines in a file results in a type error:
Prelude> (lines . readFile) "quux.txt"
<interactive>:26:10: error:
• Couldn't match type ‘IO String’ with ‘[Char]’
Expected type: FilePath -> String
Actual type: FilePath -> IO String
• In the second argument of ‘(.)’, namely ‘readFile’
In the expression: lines . readFile
In the expression: (lines . readFile) "quux.txt"
How can I do the monad trick here?
You can't compose them, at least not with (.) alone. You can use fmap (or its operator version <$>), though:
lines <$> readFile "quux.txt" -- Produces IO [String], not [String]
One way to express this in terms of a kind of composition is to first create a Kleisli arrow (a function of type a -> m b for some monad m) from lines:
-- return . lines itself has type Monad m => String -> m [String]
-- but for our use case we can restrict the type to the monad
-- we are actually interested in.
kleisliLines :: String -> IO [String]
kleisliLines = return . lines
Now you can use the Kleisli composition operator >=> to combine readFile (itself a Kleisli arrow) and lines:
import Control.Monad -- where (>=>) is defined
-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
-- Here, m ~ IO
-- a -> FilePath
-- b -> String
-- c -> [String]
(readFile >=> kleisliLines) "quux.txt"
Compare this with the >>= operator, which requires you to supply the file name to readFile before feeding the result to return . lines:
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines
>=> is natural if you are already thinking of a pipeline in terms of >=; if you want something that preserves the order of ., use <=< (also defined in Control.Monad, as (<=<) = flip (>=>); the operands are simply reversed).
(kleisliLines <=< readFile) "quux.txt"
The other answers given so far have been to make lines produce an empty monadic context, and then use monadic composition (<=<) to compose it with readFile. But you can also go the other direction: lift lines to operate through a monadic argument, and then use ordinary composition to combine it with readFile:
(fmap lines . readFile) "quux.txt"
Of course, if you're going to immediately apply this to an argument, it's simpler to just write
lines <$> readFile "quux.txt"
Related
How can I use pure functions inside IO functions? :-/
For example: I'm reading a file (IO function) and I want to parse its context, a string, by using a pure function with referential transparency.
It seems such worlds, pure functions and IO functions, are separated. How can I possibly bridge them?
The simplest way is to use fmap, which has the following type:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO implements Functor, which means that we can specialize the above type by substituting IO for f to get:
fmap :: (a -> b) -> IO a -> IO b
In other words, we take some function that converts as to bs, and use that to change the result of an IO action. For example:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
What just happened there? Well, map toUpper has type:
map toUpper :: String -> String
It takes a String as an argument, and returns a String as a result. Specifically, it uppercases the entire string.
Now, let's look at the type of fmap (map toUpper):
fmap (map toUpper) :: IO String -> IO String
We've upgraded our function to work on IO values. It transforms the result of an IO action to return an upper-cased string.
We can also implement this using do notation, to:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
It turns out that every monad has the following property:
fmap f m = do
x <- m
return (f x)
In other words, if any type implements Monad, then it should always be able to implement Functor, too, using the above definition. In fact, we can always use the liftM as the default implementation of fmap:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM is identical to fmap, except specialized to monads, which are not as general as functors.
So if you want to transform the result of an IO action, you can either use:
fmap,
liftM, or
do notation
It's really up to you which one you prefer. I personally recommend fmap.
You can also consider liftM function from Control.Monad.
A little example to help you (run it into ghci as you are under the IO Monad)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
The actual answer is as follows:
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return wraps it in the appropriate monad so that do can assign it to val.
Alex Horsman helped me. He said:
"Perhaps I'm misunderstanding, but that sounds pretty simple?
do {x <- ioFunc; return (pureFunc x)}"
And then I solved my problem:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}
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.
I found out that words is the appropriate function to split a String:
words :: String -> [String]
What's special about fmap that makes this code work:
Prelude> fmap words getLine
abc def ghi
["abc","def","ghi"]
and it's omission results in a error:
Prelude> words getLine
<interactive>:10:7: error:
• Couldn't match type ‘IO String’ with ‘[Char]’
Expected type: String
Actual type: IO String
• In the first argument of ‘words’, namely ‘getLine’
In the expression: words getLine
In an equation for ‘it’: it = words getLine
I know that fmap works with Functors but I don't know about Functors yet. Is it related to that?
getLine returns an IO String, so words getLine is a type error. fmap has type
Functor f => (a -> b) -> f a -> f b
and IO has a functor instance so fmap for IO has type (a -> b) -> IO a -> IO b. fmap words therefore has type IO String -> IO [String], and applying this to the result of getLine results in an IO [String].
In ghci, IO actions are executed an their results printed, which is why you see the result list displayed.
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:
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
How can I use pure functions inside IO functions? :-/
For example: I'm reading a file (IO function) and I want to parse its context, a string, by using a pure function with referential transparency.
It seems such worlds, pure functions and IO functions, are separated. How can I possibly bridge them?
The simplest way is to use fmap, which has the following type:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO implements Functor, which means that we can specialize the above type by substituting IO for f to get:
fmap :: (a -> b) -> IO a -> IO b
In other words, we take some function that converts as to bs, and use that to change the result of an IO action. For example:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
What just happened there? Well, map toUpper has type:
map toUpper :: String -> String
It takes a String as an argument, and returns a String as a result. Specifically, it uppercases the entire string.
Now, let's look at the type of fmap (map toUpper):
fmap (map toUpper) :: IO String -> IO String
We've upgraded our function to work on IO values. It transforms the result of an IO action to return an upper-cased string.
We can also implement this using do notation, to:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
It turns out that every monad has the following property:
fmap f m = do
x <- m
return (f x)
In other words, if any type implements Monad, then it should always be able to implement Functor, too, using the above definition. In fact, we can always use the liftM as the default implementation of fmap:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM is identical to fmap, except specialized to monads, which are not as general as functors.
So if you want to transform the result of an IO action, you can either use:
fmap,
liftM, or
do notation
It's really up to you which one you prefer. I personally recommend fmap.
You can also consider liftM function from Control.Monad.
A little example to help you (run it into ghci as you are under the IO Monad)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
The actual answer is as follows:
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return wraps it in the appropriate monad so that do can assign it to val.
Alex Horsman helped me. He said:
"Perhaps I'm misunderstanding, but that sounds pretty simple?
do {x <- ioFunc; return (pureFunc x)}"
And then I solved my problem:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}