How to avoid working in the IO monad for this entire program?

I am writing a Sokoban program in Haskell using the Gloss library, and I get to the point where I would like when a player hits a level, loads to a new level from a text file and has the program continue.

I have a bit of difficulty with this due to the limitations of Gloss - the play function for setting up the game and updating it continuously is as follows:

 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 () 

(Copied directly from http://hackage.haskell.org/packages/archive/gloss/1.7.4.1/doc/html/Graphics-Gloss-Interface-Pure-Game.html )

Here is the world type that I use:

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

where Level contain blocks at the current level. I read in Game , using something like this (I have not done a generalization yet, but this, in fact, is all that would be with the file name argument):

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

So, my difficulty is due to the update features in play . I can easily take a Game and create a Picture (and a Game , etc.) without having to read any data from the file system, if I just work on the same level, but since I'm loading levels from files in the middle of the game, I don’t know how to avoid all my Game IO Game s. Maybe this is not possible in this case, and maybe it is for a good reason? I will always work with the Game extracted from the file, but I do not know if it can be avoided at any given point, and if so, I would like to avoid it.

+6
source share
1 answer

I ended up using Gloss playIO from Graphics.Gloss.Interface.IO.Game . I needed to change a couple of my functions to work with pure data types and output them wrapped in the IO monad. Here are the types that I had to change:

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

For the most part, this only led to the addition of some return to my existing functions, but in the end it added a lot of functionality (for example, saving on the fly, loading new levels, using BMP files for graphics, etc.). I was also able to save almost all of my existing code without IO , as new functions still occupied pure data types as parameters. This turned out to be a very simple refactor and solved my problem perfectly.

+3
source

All Articles