user6542453
user6542453

Reputation: 101

Couldn't convert an IO String to [Char] in Haskell

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

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

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

Cubic
Cubic

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

Related Questions