Question

Here is my code:

getMove :: Board -> Player -> IO (Maybe (Move, Board, Player))

completeUserTurn :: Player -> Board -> Maybe (IO (Board, Player))
completeUserTurn player board = do
    m <- getMove board player --error is here
    if isNothing m then
        Nothing
    else do
        let (move, updatedBoard, updatedPlayer) = fromJust m
        if isMoveValid board move then do
            continue <- prompt $ displayToUserForPlayer updatedBoard updatedPlayer ++ "\n" ++ "Is this correct? (y/n): "
            if continue == "y" then
                return (updatedBoard, updatedPlayer)
            else
                completeUserTurn player board
        else do
            putStr "Invalid Move!\n"
            completeUserTurn player board

Here is the error I am getting (On the indicated line):

Couldn't match expected type `Maybe t0'
                with actual type `IO (Maybe (Move, Board, Player))'
    In the return type of a call of `getMove'
    In a stmt of a 'do' block: m <- getMove board player
    In the expression:
      do { m <- getMove board player;
           if isNothing m then
               Nothing
           else
               do { let ...;
                    .... } }

What is wrong? I though the <- would do the IO action and put the result in m? Why does it expect a Maybe then?

Was it helpful?

Solution

General advice

Once you've run an IO operation like getMove your function has to have type IO ????. Anything that does interaction with the outside world lives in the IO monad, so your function has to have type

completeUserTurn :: Player -> Board -> IO (Maybe (Board, Player))

not

completeUserTurn :: Player -> Board -> Maybe (IO (Board, Player))

The only way to have type Maybe (IO ???) is to not actually do any IO:

continue :: Bool -> Maybe (IO String)
continue False = Nothing
continue True = Just (putStrLn "Hooray! Please enter a string: " >> getLine)

this function doesn't actually do the getLine and isn't very useful, as you can check by doing

if isNothing (continue True) then putStrLn "nope" else putStrLn "yes"

in ghci: it never says Hooray. More useful would be

continue :: Bool -> IO (Maybe String)
continue False = return Nothing
continue True = do
   putStrLn "Hooray! Please enter a string:\n"
   xs <- getLine
   return (Just xs)

(Try continue True and continue False in ghci)

This one actually does the IO, so it has to have type IO ???.

Your code

Anyway, your function is better expressed as

completeUserTurn :: Player -> Board -> IO (Maybe (Board, Player)) -- new type
completeUserTurn player board = do
    m <- getMove board player 
    if isNothing m then
        return Nothing  -- edit #1
    else do
        let (move, updatedBoard, updatedPlayer) = fromJust m
        if isMoveValid board move then do
            continue <- prompt $ displayToUserForPlayer updatedBoard updatedPlayer ++ "\n" ++ "Is this correct? (y/n): "
            if continue == "y" then
                return $ Just (updatedBoard, updatedPlayer)  -- edit #2
            else
                completeUserTurn player board
        else do
            putStr "Invalid Move!\n"
            completeUserTurn player board

Both edit #1 and edit #2 are because of the unavoidable change of type to IO (Maybe ??).

OTHER TIPS

IO (Maybe a) is of course a very common type, but it's cumbersome to work with it directly. However, it is equivalent to MaybeT IO a, using the MaybeT monad transformer. With that, your code becomes just

import Control.Monad.Trans
import Control.Monad.Trans.Maybe

getMove' :: Board -> Player -> MaybeT IO (Move, Board, Player)
getMove' board = MaybeT . getMove board    -- with your original `getMove`

completeUserTurn :: Player -> Board -> MaybeT IO (Board, Player)
completeUserTurn player board = do
    (move, updatedBoard, updatedPlayer) <- getMove' board player
    if isMoveValid board move then do
        continue <- lift . prompt
             $ displayToUserForPlayer updatedBoard updatedPlayer ++ "\n"
                    ++ "Is this correct? (y/n): "
        if continue == "y" then
            return (updatedBoard, updatedPlayer)
        else
            completeUserTurn player board
    else do
        putStr "Invalid Move!\n"
        completeUserTurn player board

as you see, this gets us rid of all the nasty explicit Nothing checking.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top