1

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?

user6542453
  • 101
  • 7
  • 3
    The second line in your `do` block isn’t doing any I/O, so the result is pure, and you shouldn’t use the `<-` to indicate a monadic bind. Instead, write `let sUp = [toUpper c | c <- s]`, or even better, ditch the list comprehension and just write `let sUp = map toUpper s`. – Alexis King Jan 21 '17 at 10:47

2 Answers2

2

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].

Cubic
  • 14,902
  • 5
  • 47
  • 92
0

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
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 5
    I’m not entirely convinced the latter example is “more Haskell style”, though it might appeal to a specific subgroup of Haskellers. I think the first is more clear, especially for a learner, though I would still suggest `map` over the list comprehension. – Alexis King Jan 21 '17 at 10:57
  • 1
    @AlexisKing: I provided some information regarding `map` and `>>=`. Furthermore note that a significant part of the Haskell community [*considers `do` harmful](https://wiki.haskell.org/Do_notation_considered_harmful). It gives the idea that `do` is something special whereas it is only monadic calls behind the curtains. – Willem Van Onsem Jan 21 '17 at 11:00
  • 2
    I think that wiki article’s existence does indeed demonstrate that *some* people in the Haskell community consider `do` harmful, but a single wiki page doesn’t give much indication that a “significant portion” agrees with its contents. It’s a wiki, after all. – Alexis King Jan 21 '17 at 11:02
  • @AlexisKing: see also [this](http://syntaxfree.wordpress.com/2006/12/12/do-notation-considered-harmful/), and [this](http://stackoverflow.com/questions/16726659/should-do-notation-be-avoided-in-haskell). I think the main problem with `do` is that people first of all think that monads are only useful for IO, etc. whereas one can do impressive things with them from the moment they realize it is all functional programming behind it. – Willem Van Onsem Jan 21 '17 at 11:06
  • 7
    I don’t take umbrage at the alternative suggestion, I just think implying that much of the Haskell community finds `do` unidiomatic and something to be avoided is misleading. Your second example has another implicit transformation in it, which removes the explicit binding of `s`. As a rather seasoned Haskeller, I would probably write it your way, but I would not have any problem with the version using `do`, and I think it is overzealous to suggest that someone is not writing “real” Haskell if they use `do`. – Alexis King Jan 21 '17 at 11:10
  • 1
    There's nothing wrong with `do`, the problem is primarily with its usage in _teaching_. If you understand how the `do` syntax operates there's no reason not to use it, it _is_ much more convenient for writing and reading most of the time. – Cubic Jan 21 '17 at 11:41
  • 1
    @WillemVanOnsem One of your links for evidence that `do` is considered harmful is a Stack Overflow question asking if it is harmful, and *none* of the answers support that conclusion, and to the contrary point out that `do` makes longer monadic compositions more readable. – chepner Jan 21 '17 at 13:56