I want to write functions and put result to string.
I want function:
read' :: FilePath -> String
I use:
:t readFile
readFile :: FilePath -> IO String
I make:
read' :: IO ()
read' = do
str <- readFile "/home/shk/workspace/src/test.txt"
putStrLn str
I want to ask str is string or not?
We know that:
:t putStrLn
putStrLn :: String -> IO ()
Then why i can't:
read' :: String
read' = do
str <- readFile "/home/shk/workspace/lxmpp/src/test.txt"
str
I get error that:
Couldn't match expected type `[t0]' with actual type `IO String'
In the return type of a call of `readFile'
In a stmt of a 'do' expression:
str <- readFile "/home/shk/workspace/lxmpp/src/test.txt"
In the expression:
do { str <- readFile "/home/shk/workspace/src/test.txt";
str }
Thank you.
I think no one has answered this, very important question, yet:
I want to ask str is string or not?
I will try to.
The type of the variable str
is String
, yes.
However, the scope of this variable is very limited. I think desugaring the do-notation is necessary for understanding:
read' = readFile "/home/shk/workspace/src/test.txt" >>= (\str -> putStrLn str)
I think here it becomes more clear why str
is not good enough. It is an argument of the function you pass to >>=
. Its value only becomes available when someone calls your function, which happens only when the IO
action containing it is being executed.
Also, the type of read' :: IO ()
is determined not so much by the putStrLn str
, but rather by the return type of the operator >>=
. Have a look at it (specialized to the IO
monad):
(>>=) :: IO a -> (a -> IO b) -> IO b
You can see that the result is always an IO b
action, so trying to change any of arguments won't help.
You can read some monad tutorial if you want to understand why the type is the way it is. The intuition behind it is: you can't perform an action without performing an action.
And on the practical side of the question, to use the value returned by some action, instead of trying to do use (extractValue inputAction)
, which does not make sense because extractValue
is not possible, try inputAction >>= use
if your use
does involve I/O, or fmap use inputAction
if it does not.
Just to quibble a bit more, while the other answers are perfectly correct, I want to emphasize something: Something with the type IO String
isn't just a string that the type system won't let you get at directly. It's a computation that performs I/O to get a string for you. Applying readFile
to a file path doesn't return a String
value any more than putting a steak next to a meat grinder magically turns them into a hamburger.
When you have some code like this:
foo = do let getStr = readFile "input.txt"
s1 <- getStr
s2 <- getStr
-- etc.
That doesn't mean you're "taking the string out of getStr
twice". It means you're performing the computation twice and can easily get different results between the two.
You should use return str
in read'
if you want it to return str
instead of ()
. You can't strip IO
from the type of read'
, since it's not a pure function. To get a better grip on how input/output in Haskell works I recommend you to read a tutorial.
As a more detailed reason why: It allows impurity.
You absolutely can not perform IO during a pure operation, or it would completely break referential transparency. Technically you can use unsafePerformIO
but it would break referential transparency in this case - you should only use that if you can guarantee that the result is always the same.