我有一个模块Game
,用于定义类似play :: Board -> Move - > Board
的方法播放。
我想在另一个名为Playing
的模块中使用State Monad来导入Game
模块,以便我可以在循环中调用play
,直到Board
到达某个州。
我想通过播放我从State Monad获得的Board
来调用该方法,然后使用Board
返回的play
更新State Monad值。
因此,当循环继续时,我希望接收应用于play
方法和当前状态的移动。
但我完全迷失了,因为模块Game
不知道我使用的是State Monad。
我一直在查看相当多的教程和示例(例如this,this,this等),我觉得我理解State Monad的应用方式那里但显然不够好,不能将它抽象到这个特定的实现。
playing :: IO ()
playing = do
putStr $ "The board looks like:"
board <- get
putStr $ showBoard board
putStr $ "Indicate a move:"
move <- getLine
if validMove move then do
newBoard <- play board (getMove move)
if gameEnded newBoard then do
putStr $ "You win!" --stop the execution
else do
put newBoard
else do
putStr $ "Invalid move"
我希望playing
处于循环状态,直到获得特定Board
表示游戏结束。并使用State Monad将当前Board
发送到play
以及Game
模块中的其他方法,例如gameEnded :: Board -> Bool
,showBoard :: Board -> String
和'getMove ::字符串 - &gt;移动”。
欢迎任何帮助
答案 0 :(得分:1)
如果您将参数的顺序交换为play
,则您的函数类型为:
Move -> Board -> Board
您可以使用Move
部分申请获得以下类型之一:
Board -> Board
您可以使用State
将此转换为modify :: (s -> s) -> State s ()
上的操作来修改电路板:
playing :: Move -> State Board ()
playing move = modify (play move)
这里的一个解决方案是monad变压器 - 声音比它更可怕。您可以使用StateT
而非IO
,StateT
来存储游戏状态,使用IO
来提示用户进行移动。例如:
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (evalStateT, gets, modify)
-- Get a move from the user.
getMove :: IO Move
getMove = do
line <- getLine
-- (Your implementation of parsing moves here.)
-- The initial state of the board.
initialBoard :: Board
initialBoard = -- ...
-- Whether the board represents a completed game.
boardDone :: Board -> Bool
boardDone board = -- ...
-- Main game loop.
gameLoop :: IO ()
gameLoop = evalStateT loop initialBoard
where
loop = do
move <- lift getMove
modify (play move)
done <- gets boardDone
if done then pure () else loop
您使用lift
将正常IO
操作转换为StateT Board IO
操作,modify :: (Monad m) => (s -> s) -> StateT s m ()
修改状态,使用gets :: (Monad m) => (s -> a) -> StateT s m a
读取属性当前状态。 loop
要么尾部调用自己继续播放,要么返回。
使用编辑过的问题中的结构和名称:
playing :: IO ()
playing = evalStateT loop initialBoard
where
loop :: StateT Board IO ()
loop = do
printBoard
move <- lift promptMove
modify (play move)
ended <- gets gameEnded
if ended
then lift $ putStrLn "You win!"
else loop
printBoard :: StateT Board IO ()
printBoard = do
lift $ putStrLn $ "The board looks like:"
board <- get
lift $ putStrLn $ showBoard board
promptMove :: IO Move
promptMove = do
putStr "Indicate a move: "
move <- getLine
if validMove move
then pure $ getMove move
else do
putStrLn "Invalid move."
promptMove