0

这是我的代码:

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 中?为什么它期望一个Maybethen?

4

2 回答 2

3

一般建议

一旦你运行了一个IO操作,比如getMove你的函数必须有 type 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 中:它从不说万岁。更有用的是

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,所以它必须有 type 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 ??).

于 2012-11-11T08:44:30.780 回答
0

IO (Maybe a)当然是一种很常见的类型,但是直接使用它很麻烦。但是,它等同于MaybeT IO a使用MaybeTmonad 转换器。这样,您的代码就变成了

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检查。

于 2012-11-11T09:55:40.817 回答