Способ работы MVars заключается в том, что значение типа MVar Bool
является непрозрачным «токеном», ссылающимся на место хранения для Bool
.Вы создаете такой токен, считываете и изменяете содержимое связанного с ним места хранения, используя действия ввода-вывода.
Сам токен (значение MVar Bool
) по умолчанию не имеет экземпляра Show
.В целях отладки вы можете добавить одно:
instance Show (MVar a) where show _ = "<MVar>"
Это позволит вам получить экземпляр Show
для PongGame
без получения сообщения об ошибке.Тем не менее, значения, хранящиеся в MVars
, не могут отображаться экземпляром Show
без какого-либо вопиющего злоупотребления вводом-выводом, поэтому вы можете подумать о написании функции dumpGame :: PongGame -> IO ()
, которая симпатично печатает текущее состояние игры со всемиЗначения MVar, позволяющие полностью пропустить экземпляр Show
.
В любом случае, переписать вашу программу для использования MVars в ключевых PongGame
полях:
data PongGame = Game
{
...
, wHeld :: MVar Bool -- segura o w
, sHeld :: MVar Bool -- segura os
, downHeld :: MVar Bool -- segura down
, upHeld :: MVar Bool -- segura para cima
...
}
, которые вы захотите переписатьВаш movePaddle
и его подфункции для запуска в монаде ввода-вывода:
movePaddle :: PongGame -> IO PongGame
moveLeftPaddle :: PongGame -> IO PongGame
moveRightPaddle :: PongGame -> IO PongGame
В movePaddle
вы можете заменить оператор .
на <=<
из Control.Monad
, что является монадическим эквивалентомкомпозиции функций:
movePaddle = moveLeftPaddle <=< moveRightPaddle
Это в основном сокращение для:
movePaddle game = do
game1 <- moveRightPaddle game
game2 <- moveLeftPaddle game1
return game2
Затем вам нужно будет переписать moveLeftPaddle
и moveRightPaddle
, чтобы получить доступ к содержимому MVarsвыполняя действия ввода-вывода:
moveLeftPaddle game
= do up <- readMVar (wHeld game)
dn <- readMVar (sHeld game)
case (up, dn) of
(True, False) -> return $ game {playerLPos = paddleUp (playerLPos game)}
(False, True) -> return $ game {playerLPos = paddleDn (playerLPos game)}
_ -> return game
с moveRightPaddle
, определенным аналогично.
Для ясности, вызов функции wHeld game
- это простой, чистый вызов функции, который извлекает токен типа MVar Bool
, связанный с wHeld
поле.Затем мы выполняем действие ввода-вывода readMVar <this_token>
для фактического получения значения Bool
, которое мы затем можем использовать в операторе case
для обновления состояния игры.
В другом месте вашей программы вынужна подпрограмма setup
, которая создает эти MVars:
initGame :: IO PongGame
initGame = do
...
wHeld <- newMVar False
sHeld <- newMVar False
...
return $ Game ... wHeld sHeld ...
, и у вас, вероятно, будет какой-то поток, выполняющий такую функцию:
processKeyEvent :: KeyEvent -> PongGame -> IO ()
processKeyEvent event game = do
...
case event of
...
KeyDown 'W' -> void $ swapMVar (wHeld game) True
KeyUp 'W' -> void $ swapMVar (wHeld game) False
...