Reputation: 101
I wrote this simple function that takes two file names (String) and wrote the content of the first file into the second file applying toUpper
to each character.
import Data.Char
ioFile f1 f2 = do
s <- readFile f1
sUp <- [toUpper c | c <- s]
writeFile f2 sUp
But the interpreter raise an error
Couldn't match expected type ‘IO String’ with actual type ‘[Char]’
In a stmt of a 'do' block: sUp <- [toUpper c | c <- s]
In the expression:
do { s <- readFile f1;
sUp <- [toUpper c | c <- s];
writeFile f2 sUp }
In an equation for ‘ioFile’:
ioFile f1 f2
= do { s <- readFile f1;
sUp <- [toUpper c | c <- s];
writeFile f2 sUp }
How can I use s
as a [Char]
instead of IO String
?
Upvotes: 1
Views: 509
Reputation: 476584
The second line is not an IO Monadic operation, it is simply list comprehension, so you should write it as:
import Data.Char
ioFile f1 f2 = do
s <- readFile f1
writeFile f2 [toUpper c | c <- s]
A significant part of the Haskell community however considers do
harmful and thus would prefer to see:
ioFile f1 f2 = readFile f1 >>= \s -> writeFile f2 $ map toUpper s
or even shorter:
ioFile f1 f2 = readFile f1 >>= writeFile f2 . map toUpper
with map :: (a -> b) -> [a] -> [b]
you apply the given function (in this case toUpper
) to every element of the given list and generate a list of results. (>>=) :: Monad m => m a -> (a -> m b) -> m b
informally is equivalent to calling the given right operand function on the "result" of the left monad. So c >>= f
is equivalent to:
do
x <- c
f x
Upvotes: 0
Reputation: 15673
You can't bind a 'pure' value like sUp <- [toUpper c | c <- s]
this way. Note that unlike what the accepted answer does suggest, that doesn't mean you can't give it a name. For one, there's the return
function that lifts a pure value to IO
(or any monad really, but we're in IO
right now), so sUp <- return [toUpper c | c <- s]
does work. There's also a variant of the let syntax that pretty much does that: let sUp = [toUpper c | c <- s]
.
Upvotes: 2