0

I want to extract the values out of the monad but i can't figure out ,how can i do that?

The problem is in available function.

There are already few answers but here i have specific question about an extra function which only tries to get value from IO monad.

main2 = do
        fileName <- getLine
        case (available fileName) of
            False -> "Could not find the file"
            True ->   withFile fileName ReadMode (\handle -> do
                            contents <- hGetContents handle
                            putStrLn $ "Enter the name of second file"
                            secondName <- getLine
                            case (available fileName) of
                                False -> "could not find second file"
                                True -> withFile secondName AppendMode (\secondHandle -> do
                                                hPutStrLn secondHandle contents
                                                putStrLn $ "Done, copying file"))

Here is my available function and i want simple True or False. I think i can't do pattern matching with IO and also can't figure out how do i get simple True or False?

available :: FilePath -> Bool
available fileName  = case (doesFileExist fileName) of
                        IO True = True
                        IO False = False
Khan Saab
  • 434
  • 1
  • 8
  • 20
  • 4
    You can't*, as that would entirely defeat the purpose of the IO Monad. Don't "taint" values with IO in the first place if you can help it. – Carcigenicate Jan 17 '18 at 17:33
  • 1
    You can not (at least not in general) unwrap values out of a monad, and not an IO monad as well. – Willem Van Onsem Jan 17 '18 at 17:33
  • 1
    An IO monad does *not* describes the state of a system, it describes the *change* of a system. – Willem Van Onsem Jan 17 '18 at 17:35
  • How would the above function work then? How can i check if the file exist or not? – Khan Saab Jan 17 '18 at 17:39
  • 1
    Your function would return `IO Bool`, not `Bool`. – Fyodor Soikin Jan 17 '18 at 17:48
  • You have a logical error. After checking the file for existence, it can be deleted before calling the function withFile. So your checking doesn't promise anything. – freestyle Jan 17 '18 at 18:00
  • `x :: IO Bool` represents a program `x` which can ask the user/use the disk/etc. to compute a boolean. Instead, `y :: Bool` is a program `y` which can produce the boolean without any I/O. You can turn `y` into `x` (using `return`), but not vice versa. Any monad tutorial should explain this better. – chi Jan 17 '18 at 18:46
  • The goal in writing a Haskell program is to get everything *into* IO, not to take anything *out*. – Rein Henrichs Jan 18 '18 at 05:07
  • The entire point of `IO` is that you cannot take things out of it. – user253751 Jan 19 '18 at 00:52

1 Answers1

4

Use another "bind" (<-) to "get" the value. It's important to appreciate that <- is not magic; it repesents a call to the >>= function which we use to compose functions of IO values.

-- base
import System.IO (withFile, IOMode (..), hPutStrLn, hGetContents)

-- directory
import System.Directory (doesFileExist)

main2 :: IO ()
main2 = do
  fileName <- getLine
  available1 <- doesFileExist fileName
  case available1 of
    False -> putStrLn "Could not find the file"
    True  -> withFile fileName ReadMode $ \handle -> do
      contents <- hGetContents handle
      putStrLn "Enter the name of second file"
      secondName <- getLine
      available2 <- doesFileExist fileName
      case available2 of
        False -> putStrLn "could not find second file"
        True  -> withFile secondName AppendMode $ \secondHandle -> do
          hPutStrLn secondHandle contents
          putStrLn "Done, copying file"

Your extra function available is not possible to implement, because the type doesn't accurately describe what you want it to mean.

FilePath -> Bool is just a function, a mapping from strings to booleans. It does not involve the contents of anyone's filesystem. A function is a mathematical function; it always evaluates to the same result, no matter whose computer it is evaluated on, or even if we evaluate it ourselves with pencil and paper.

doesFileExist :: FilePath -> IO Bool is a mapping from strings to I/O actions which produce a boolean. IO values represent computational effects, not abstract mathematical objects, and so these can represent concepts like "look at this computer's filesystem."

You can never "get out of IO," but fortunately you don't need to, because the type of main2 is IO () anyway. Your program is built of I/O actions composed with the >>= combinator.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • I tried to use this operator in available function and that still does not work. Why do i have to use this operator in same function? – Khan Saab Jan 17 '18 at 17:46
  • The `<-` operator only works within a `do` block. But there's a larger conceptual problem with the type of your `available` function. – Chris Martin Jan 17 '18 at 17:48
  • @KhanSaab You could use it in `available` like this: `available filename = do result <- doesFileExist fileName; return result`, but that would be completely equivalent to `available filename = doesFileExist filename`, so it serves little purpose. What you can't do is define a function that does IO and then has a result type other than `IO something`. – sepp2k Jan 17 '18 at 17:53
  • @KhanSaab The thumb rule is that you can only use `<-` in a `do` block, which has to end with a value of type `IO something`. I.e., (roughly) you can not take something out of IO, unless you will use it to compute something inside IO at the end. – chi Jan 17 '18 at 18:43