Haskell使用monads作为一种全局变量

时间:2018-02-25 14:28:59

标签: haskell

我正在构建一个蛇游戏,我正在为游戏使用光泽图形。 我正在使用G.play开始游戏:

main :: IO ()
main = do
rand <- R.randomIO
let world = startWorld rand

G.play
    (displayMode world)
    backgroundColor
    stepRate
    world
    drawWorld
    handleEvent
    handleStep

现在蛇基本上是坐标对的列表,分数是蛇的长度。但是,我添加了一个特殊苹果的功能,它给你2分而不是1分,并且只为蛇增加了1个长度。但是因为得分是基于蛇的长度我想创建一个全局变量,它将计算蛇吃掉特殊苹果的次数并将其添加到得分中。我搜索了网络,无法弄清楚如何实际使用Monad.State来帮助我使用这个全局变量。

1 个答案:

答案 0 :(得分:0)

如果您希望同时访问IO和State,则可以使用StateT。 State使用完全相同的接口减去类型构造函数中的额外参数。

import Control.Monad.Trans.State
import Control.Monad.IO.Class

游戏类型是应用于IO的状态转换器。这将状态包含在IO类型中,允许我们在需要时将lift IO函数插入IO monad。这里s是我们的开始状态,a是我们的返回状态。

type Game s a = StateT s IO a

捕获游戏状态作为记录类型,其中包含您要随时更新的字段。这是您在全局游戏状态下存储蛇,长度和其他项目的地方。

data GameState = GameState
  { _score :: Int
  , _length :: Int
  } deriving (Show)

要修改状态,我们使用modify中的StateT函数获取状态,在其上应用函数,然后设置结果状态(modify fn = do s <- get; put (fn s)

set :: (GameState -> GameState) -> Game GameState ()
set field = modify field

score :: (Int -> Int) -> GameState -> GameState
score fn (GameState s l) = GameState (fn s) l

len :: (Int -> Int) -> GameState -> GameState
len fn (GameState s l) = GameState s (fn l)

要使用IO,我们必须使用liftIO将函数提升为IO monad

display :: Show a => a -> Game GameState ()
display = liftIO . print

以下是一个使用它的例子。

game :: Game GameState ()
game = do s <- gets _score
          l <- gets _length
          display "initial values"
          display s
          display l
          set $ score (+2)
          set $ len (+1)
          s <- gets _score
          l <- gets _length
          display "modified values"
          display s
          display l

runStateT game (GameState 0 0)将打印

"initial values"
0
0
"modified values"
2
1
((),GameState (_score = 2, _length = 1})

哪个适合您现有的模型并进行细微更改。