Home > Software design >  How can I use the result of an IO action in an non-IO-function?
How can I use the result of an IO action in an non-IO-function?

Time:01-17

This function takes an filepath and returns the content of the file. this file includes a cuple of lines of the same length. -- It's used to be an primitive picture.

parsePicture :: FilePath -> IO()
parsePicture fileName = do
    content <- lines <$> readFile fileName
    print content

Now I've tried to implement a function to flip the "picture" vertically:

type Picture = [[Char]]

flipVertical :: IO() -> Picture
flipVertical xs = map reverse xs

But I only get the following error code:

zuP12.hs:24:31: error:
    • Couldn't match expected type ‘[[Char]]’ with actual type ‘IO ()’
    • In the second argument of ‘map’, namely ‘xs’
      In the expression: map reverse xs
      In an equation for ‘flipVertical’: flipVertical xs = map reverse xs
   |
24 | flipVertical xs = map reverse xs
   |                               ^^
Failed, no modules loaded.

How can I use my function flipVertical on the result of parsePicture?

CodePudding user response:

This function... returns the content of the file.

No it doesn't. It prints the content of the file, to STDOUT. For most purposes you should consider information you put to STDOUT to be gone – the information has left your program and is now on the terminal screen, or whereever else the user chooses to put it, but not accessible to the program anymore.

(Strictly speaking, it is possible to redirect STDOUT back into your own program, but it's a big hack, don't do this.)

Instead, you should change the function so it actually does return the content:

parsePicture :: FilePath -> IO Picture
parsePicture fileName = do
    content <- lines <$> readFile fileName
    return content

...or simply

parsePicture fileName = lines <$> readFile fileName

which behaves exactly the same (by the monad laws).

Also, I would rather call this loadPicture: parsing shouldn't involve file reading.

Of course, you can still print the contents later on, with something like

main = do
   ...
   fooPicture <- parsePicture "foofilename"
   print fooPicture
   ...

As for flipVertical, this shouldn't have anything to do with IO at all. It's simply a pure function

flipVertical :: Picture -> Picture
flipVertical xs = map reverse xs

which can be used like, for example

main = do
   ...
   fooPicture <- parsePicture "foofilename"
   print $ flipVertical fooPicture
   ...

or

main = do
   ...
   fooVFPicture <- flipVertical <$> parsePicture "foofilename"
   ...
  • Related