使用Maybe expect编译do块中的错误

时间:2012-11-11 07:31:28

标签: haskell io maybe

这是我的代码:

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

这是我得到的错误(在指示的行上):

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 ...;
                    .... } }

有什么问题?我虽然<-会执行IO操作并将结果放入m?为什么期望Maybe呢?

2 个答案:

答案 0 :(得分:3)

一般建议

运行IO getMove之类的操作后,您的函数必须具有IO ????类型。任何与外界进行交互的东西都存在于IO monad中,因此你的函数必须有类型

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

不是

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

使用类型Maybe (IO ???)的唯一方法是不实际执行任何IO:

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

此函数实际上并不执行getLine并且不是很有用,因为您可以通过执行检查

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

在ghci:它从未说过Hooray。更有用的是

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

(在ghci中尝试continue Truecontinue False

这个实际上是IO,所以它必须有IO ???类型。

您的代码

无论如何,你的功能更好地表达为

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

编辑#1 编辑#2 都是因为类型不可避免地更改为IO (Maybe ??)

答案 1 :(得分:0)

IO (Maybe a)当然是一种非常常见的类型,但直接使用它很麻烦。但是,使用MaybeT monad transformer相当于MaybeT IO a。这样,您的代码就变成了

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

如你所见,这让我们摆脱了所有讨厌的明确Nothing检查。