如何避免在整个程序中使用IO monad?

时间:2013-03-28 20:39:48

标签: haskell io

我正在使用Gloss库在Haskell中编写Sokoban程序,并且我达到了我想要的程度,当玩家击败关卡时,从文本文件加载到新关卡并拥有程序继续。

由于Gloss的限制,我有一点点困难 - 设置游戏的play功能让它不断更新如下:

play    :: forall world
        .  Display                      -- ^ Display mode.
        -> Color                        -- ^ Background color.
        -> Int                          -- ^ Number of simulation steps to take for each second of real time.
        -> world                        -- ^ The initial world.
        -> (world -> Picture)           -- ^ A function to convert the world a picture.
        -> (Event -> world -> world)    -- ^ A function to handle input events.
        -> (Float -> world -> world)    -- ^ A function to step the world one iteration.
                                        --   It is passed the period of time (in seconds) needing to be advanced.
        -> IO ()

(直接从http://hackage.haskell.org/packages/archive/gloss/1.7.4.1/doc/html/Graphics-Gloss-Interface-Pure-Game.html复制)

以下是我正在使用的world类型:

data Game = Game
  { levelNumber  :: Int,
    currentLevel :: Level Square,
    won          :: Bool }

其中Level包含当前级别的块。我正在使用类似的东西在Game中读取(实际上还没有一个广义的,但这基本上就是文件名参数):

startGame = do
  lvl <- readFile "levels/level001.lvl"
  let lvl' = parseLevel lvl
  return $ Game 1 lvl' False

所以,由于play中的更新功能,我的困难正在出现。如果我只是在单个级别上操作,我可以轻松地获取Game并生成Picture(以及Game等),而无需从文件系统中读取任何数据,但由于我在游戏过程中加载文件中的级别,我不知道如何避免制作Game IO Game的所有内容。也许在这种情况下这是不可能的,也许这是有充分理由的?我始终在从文件中提取的Game上运行,但我不知道在任何给定点是否可以避免,如果是,我想避免它。

1 个答案:

答案 0 :(得分:3)

我最终使用来自Graphics.Gloss.Interface.IO.Game的Gloss playIO。我需要更改我的几个函数来操作纯数据类型,并将它们包装在IO monad中。以下是我必须改变的类型:

worldToPicture :: World -> IO Picture
eventHandler   :: Event -> World -> IO Picture
stepWorld      :: Float -> World -> IO World 

在大多数情况下,这只会导致在我当前现有的功能中添加一些return,但它最终添加了很多功能(例如即时保存,加载新级别,使用BMP文件图形等)。我还能够保留几乎所有现有代码IO,因为新函数仍将纯数据类型作为参数。它最终成为一个非常简单的重构,完美地解决了我的问题。