2
votes

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.

3
What is the error message? This code looks fine.4castle
you can't compare a char literal to an IO Char directly Apparently the code isn't doing thatn. 1.8e9-where's-my-share m.
Added error message to questionJames Strieter
Your error message seems to be about completely different code than what you posted. Please have code exemplifying your actual problem in your question.Cubic
That's a good point. The errant code was for a related but different issue, so I will create a separate question for that. My apologies I'm new at this.James Strieter

3 Answers

8
votes

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.

5
votes

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
2
votes

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 every call the function f.

Instead, it just creates a new IO action that wraps both m and f and, when executed, finally executes m, extracts the return value, and calls f with the result.

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."