Reputation: 819
I know you're supposed to wrap up the operations you want to perform on a result in a monad rather than unwrap things from the monad.
What I can't find are any idiot-friendly examples of how to do that.
For example, I want to do something like this:
myFunction = do
c <- getChar
if (c == 'q')
then putStrLn "take action 1"
else putStrLn "take action 2"
But you can't compare a char literal to an IO Char directly.
GHCi version is 8.4.4.
Error Message:
[1 of 2] Compiling Lib ( /Users/jamesstrieter/hask-tink/src/Lib.hs, interpreted ) /Users/jamesstrieter/hask-tink/src/Lib.hs:66:18: error: • Couldn't match expected type ‘IO char’ with actual type ‘Char’ • In the second argument of ‘(==)’, namely ‘'q'’ In the expression: x == 'q' In an equation for ‘what2do’: what2do x = x == 'q' • Relevant bindings include x :: IO char (bound at /Users/jamesstrieter/hask-tink/src/Lib.hs:66:9) what2do :: IO char -> Bool (bound at /Users/jamesstrieter/hask-tink/src/Lib.hs:66:1) | 66 | what2do x = x == 'q' | ^^^ Failed, no modules loaded.
Upvotes: 3
Views: 1412
Reputation: 532093
One of the most important things to understand about the IO
monad is that the expression m >>= f
does not execute the action m
, nor does it ever call the function f
.
Instead, it just creates a new IO
action that wraps both m
and f
, which, when executed, will finally execute m
, extract the return value, and then execute the next action calculated on the spot by calling f
with that value.
That's it. Your entire Haskell program is nothing but a DSL for building a single IO action that gets assigned to main
, which the Haskell runtime will execute for you.
So when you write
-- Rewritten slightly for brevity
myFunction = do
c <- getChar
putStrLn (if (c == 'q')
then "take action 1"
else "take action 2")
this is desugared to
myFunction = getChar >>= (\c ->
putStrLn (if (c == 'q')
then "take action 1"
else "take action 2")
and what you are actually saying is "Build an IO
action containing getChar
and a function of type Char -> IO ()
, such that when this action is executed, it executes getChar
and passes the resulting Char
to the function to produce another IO
action to be executed immediately."
Upvotes: 2
Reputation: 8529
The code you posted looks perfectly correct and functional.
do-notation is a way of working with value in monad.
c <- getChar
within the do block binds c to the char inside the IO Char
you get with getChar
. You can compare c == 'q'
just fine here because c is a plain char, not an IO Char
.
To answer you direct question, you can use the return
function to put a pure value into any monad, including IO, so return 'q'
"wraps" the character literal 'q' into a monad. This isn't what you want in this case, the code you already have is what you are looking for.
Upvotes: 9
Reputation: 64740
But you can't compare a char literal to an IO Char directly.
Sure, but when you "bind" the result of the IO action it is no longer an IO Char
but just a Char
so that's why it works.
In more words:
Prelude> :t getChar
getChar :: IO Char
Prelude> c <- getChar
x
Prelude> :t c
c :: Char
Upvotes: 6