【问题标题】:compile error in do block with Maybe expected在可能预期的do块中编译错误
【发布时间】:2012-11-11 07:31:28
【问题描述】:

这是我的代码:

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

怎么了?我虽然 &lt;- 会执行 IO 操作并将结果放在 m 中?那为什么它会期待Maybe呢?

【问题讨论】:

  • 因为一旦你进入了 IO monad,就无法逃脱!您是否尝试过将类型签名更改为它建议的类型?
  • 你给出的类型是一个纯函数的类型,它可能会给你一个 IO 来执行,而一旦你在 do 块中使用 getMove,你就会把它变成 IO 类型(东西) .您也需要更改 Nothing 以返回 Nothing。
  • 您还需要将 return (updatedBoard, updatedPlayer) 更改为 return (Just (updatedBoard, updatedPlayer)) 以匹配。
  • completeUserTurn 的类型签名没有意义。如果有的话,它应该返回IO ( Maybe(...) )

标签: haskell io maybe


【解决方案1】:

一般建议

一旦你运行了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 中:它从不说万岁。更有用的是

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

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

【讨论】:

    【解决方案2】:

    IO (Maybe a)当然是一个很常见的类型,但是直接用起来比较麻烦。但是,它等效于MaybeT IO a,使用MaybeT monad transformer。这样,你的代码就变成了

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多