2

I have a homework to sort a numbers that are to extract from a file.

Simple File Format:

45673
57879
28392
54950
23280
...

So I want to extract [Int] and than to apply my sort-function radix to it. I write in my file

readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile
makeInteger :: [String] -> [Int]
makeInteger = map read

and then I write in the command line

radix (makeInteger (readlines("111.txt")))

and then I have, off course, problems with type conversion from IO String to String. I tried to write

makeInteger :: IO [String] -> [Int]
makeInteger = map read

but it also doesn't work.

How do I work with pure data outside the IO monad?

recursion.ninja
  • 5,377
  • 7
  • 46
  • 78
user2957954
  • 1,221
  • 2
  • 18
  • 39
  • As I wrote above, I have a problem with incompatibility of IO [String] and [String] The answer of compiler: Couldn't match expected type (String) with actual type IO String So I cannot apply function makeInteger to the results of function readlines – user2957954 Nov 05 '13 at 21:04
  • This is probably the most common question. Notice `IO [String]` is an action, not a bunch of strings (think, `getStr` from C). You RUN the action, not convert it to a string. – Thomas M. DuBuisson Nov 05 '13 at 21:05
  • I understand (somehow) the problem, but don't know how to write correctly to get [Int] in the end – user2957954 Nov 05 '13 at 21:06
  • If you start with an `IO [String]`, you can't get an `[Int]` as result. What you can do is applying a plain `[String] -> [Int]` function to values in the `IO` monad. – duplode Nov 05 '13 at 21:08
  • 1
    You're able to convert an `IO String` into an `IO [String]` using `fmap lines`. Here, `lines` has the type `String -> [String]`. You have a function with type `[String] -> [Int]`, how would you apply it to `IO [String]`? – bheklilr Nov 05 '13 at 21:14
  • I'm not sure that I understand properly what you mean. could you please write an example of code? – user2957954 Nov 05 '13 at 21:17
  • The function `readFile` has type `FilePath -> IO String`. You've composed it with the function `fmap lines`, which has the type `Functor f => f String -> f [String]`, and in this case gets turned into `IO String -> IO [String]` using IO's Functor instance (since `readLine` returns `IO String`). So, what would be the type of `fmap makeInteger` using IO's Functor instance? Since `makeInteger` has type `[String] -> [Int]`, you would get `IO [String] -> IO [Int]`. So you can compose `fmap makeInteger . readLines` to get the result you want. – bheklilr Nov 06 '13 at 01:00

1 Answers1

2

According to this, "the inability to "escape" from the monad is essential for monads like IO".

So you need to do something like:

readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile
makeInteger :: [String] -> [Int]
makeInteger = map read

main = do
  content <- readLines "111.txt"
  return (radix $ makeInteger content)

This "takes the content out" of the IO monad, applies the function you want on it, then puts it back into the IO monad again.

Community
  • 1
  • 1
Alex Appetiti
  • 493
  • 2
  • 7
  • 2
    By the way, this `do` block is equivalent to `fmap (radix . makeInteger) (readLines "111.txt")`, thus illustrating what bheklir said in the comments above. – duplode Nov 05 '13 at 21:23
  • @duplode I hope user reads your solution, it definitely is the one that makes the most sense given his readLines method... – Alex Appetiti Nov 05 '13 at 22:07