Function asking for Input in haskell (with print in do block)

762 Views Asked by At

im really new to haskell and i'm trying to implement a simple connect4 game, when I try to get the player to input a new move I want to prompt him to do so. This is the relevant code I have:

advanceHuman :: Board -> Board
advanceHuman b = do
     let column = query 
     if (snd((possibleMoves b)!!(column-1)) == cha)
         then updateBoard b p1 column
         else advanceHuman b

query :: Int
query = do {
    print ("escoge una columna vacia") ; -- choose empty column
    input <- getLine ;
    return (read input) }

as you can see I try to prompt the player, get his answer and pass it to other functions (it is assumed the player will be cooperative and input a valid number). However, this is the error message I get when I try to compile

    * Couldn't match expected type `Int' with actual type `IO b0'
    * In a stmt of a 'do' block: print ("escoge una columna vacia")
      In the expression:
        do print ("escoge una columna vacia")
           input <- getLine
           return (read input)
      In an equation for `query':
          query
            = do print ("escoge una columna vacia")
                 input <- getLine
                 return (read input)
   |
47 |     print ("escoge una columna vacia") ;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To my understanging, I should be able to print a line in a do block regardless of what that function is outputing right? Whats the issue, have I misunderstood how "do" works?

UPDT

as requested, these are the other functions involved

possibleMoves:: Board -> Board 
possibleMoves b = take sx b

updateBoard:: Board -> String -> Int -> Board
updateBoard b pl col = b

Updateboard has not been implemented yet so it just has some dummy code to appease the compiler

p1 cha and sx are global constants declared beforehand

Basically I chop off the top of my board to check which columns are full ('_' = empty), check the top of the column the player specifies with "query" to see if its a legal move; if not, the process starts again.

2

There are 2 best solutions below

2
luqui On BEST ANSWER

The theory of monads is very interesting and it is well worth your while to dive into it and understand it all deeply. But here's a mostly wrong answer to help you get started coding without worrying about all that.

There are two kinds of things, values and actions. Actions have types wrapped in IO, like IO Int, IO String, IO (Maybe [Bool]). Actions are the way you do I/O. They do some I/O, and after they are done, they return a value of the type they wrap.

Actions are constructed with do, and usually the last line has return <value> (or is another action, in which case it uses the return value of that one). So your query is an action:

query :: IO Int   -- notice the IO
query = do
    print ("escoge una columna vacia")
    input <- getLine
    return (read input)

The way you use actions is by binding them using the <-, which you have already done with getLine. This can only be done in a do block. So when you want to use query in advanceHuman, you need to bind it:

advanceHuman b = do 
    input <- query
    ...

The name (or pattern) on the left of the binding becomes a value of type whatever was wrapped in IO -- in this case Int.

However, I said that do constructs actions. This means that advanceHuman needs to return an action type:

advanceHuman :: Board -> IO Board
advanceHuman b = do 
    input <- query
    ...

The only things that can be lines in a do block are actions, either bound to values or not, and return <value> (which, as it turns out, is also an action).

You must bind actions before you use their values. E.g. if you have

getX :: IO Int
getY :: IO Int

Then you can't say getX + getY to get their sum. You have to say do { x <- getX; y <- getY; return (x + y) } (or liftA2 (+) getX getY, but let's not get ahead of ourselves).

If you want to bind a name to a value instead of an action, you use let instead. So in advanceHuman you used let when you should have used <- because query is an action.

Hope this helps.

3
d2ci8xc5 On

Haskell does not use braces to scope code. Instead it's done with indentation. return isn't like your typical return statement either that you find in imperative programming. What it's actually doing is wrapping your value in a monad type. You can check this in GHCI:

Prelude> :t return
return :: Monad m => a -> m a

the do notation is a way of composing monadic actions. It's difficult to explain what that means without a lot of theory that I won't go into here. (I suggest you pick up a haskell book). It's just syntax sugar and you go get by without it (I will show below).

To fix your code at least:

query :: (IO Int) 
query = do 
    print ("escoge una columna vacia")
    input <- getLine
    return (read input)

IO is a kind that becomes a type when Int is applied to it. The type is (IO Int)

Here's the code without the do:

module Examples where
query :: (IO Int) 
query = 
    print ("escoge una columna vacia") >>
    getLine >>= (\x ->
    return (read x))

I suggest you look up what >> and >>= do. In general you should avoid the function read it's an unsafe function (what if you had typed in a string instead of an int there?) There are other safer functions that don't assume that you have a typeclass implemented