
Hello all, I am trying to make a (turn-based) game in Haskell and need to pass around quite a bit of information, so using the State monad seems most appropriate. My question is, which is a better idea: 1) Using State GameState r and then call execState for each game event (i.e. user input) so I can do IO 2) Using StateT GameState IO () and have the entire game live in one big execStateT call. (I note XMonad does something similar.) There are difficulties with the first option, including keeping even more state about what we're doing (for instance, are we in a menu?), and adding stuff later would possibly require substantial rewrites. Other than the fact that I would have IO in places where it perhaps shouldn't be, the second option seems better, but I am new to all this and it may be that I'm missing something.

On 4/30/07, Denis Volk
Hello all,
I am trying to make a (turn-based) game in Haskell and need to pass around quite a bit of information, so using the State monad seems most appropriate. My question is, which is a better idea:
1) Using State GameState r and then call execState for each game event (i.e. user input) so I can do IO 2) Using StateT GameState IO () and have the entire game live in one big execStateT call. (I note XMonad does something similar.)
There are difficulties with the first option, including keeping even more state about what we're doing (for instance, are we in a menu?), and adding stuff later would possibly require substantial rewrites. Other than the fact that I would have IO in places where it perhaps shouldn't be, the second option seems better, but I am new to all this and it may be that I'm missing something.
I like the second option better too. Also, you can make this type opaque (stick it in a newtype, use newtype deriving to derive the appropriate classes) and only export whatever IO actions you want to support (e.g. you may want to be able to spawn off a thread in a game for concurrent AI, so you could wrap that in a function that can only fork off Game actions, and not general IO actions, also you may want to be able to store certain game properties to disk for e.g. save games, but not allow general disk writes, so you only suply a savegame abstraction). You still get a nice encapsulation of what kind of IO actions people can do, because you don't have to allow them the use of "lift" or "liftIO". -- Sebastian Sylvan +44(0)7857-300802 UIN: 44640862

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Denis Volk wrote:
There are difficulties with the first option, including keeping even more state about what we're doing (for instance, are we in a menu?), and adding stuff later would possibly require substantial rewrites. Other than the fact that I would have IO in places where it perhaps shouldn't be, the second option seems better, but I am new to all this and it may be that I'm missing something.
Are you going to be making lots of separate little sequenced changes to the state? If not, passing it around explicitly may be clearer. It may be helpful to distinguish between logical game state and the user-interface parts (such as "in a menu"), possibly putting them in different 'data', different parts of the code. Hoping that was any help, Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGNl9WHgcxvIWYTTURAr2LAJ9uGh09lTvfJ9wIRHmGszN5VQH2BwCfb2kQ 6GMUVZrggM3y1oZyk+UU49w= =kJhf -----END PGP SIGNATURE-----

On Mon, Apr 30, 2007 at 11:16:47PM +0200, Denis Volk wrote:
Hello all,
I am trying to make a (turn-based) game in Haskell and need to pass around quite a bit of information, so using the State monad seems most appropriate. My question is, which is a better idea:
1) Using State GameState r and then call execState for each game event (i.e. user input) so I can do IO 2) Using StateT GameState IO () and have the entire game live in one big execStateT call. (I note XMonad does something similar.)
There are difficulties with the first option, including keeping even more state about what we're doing (for instance, are we in a menu?), and adding stuff later would possibly require substantial rewrites. Other than the fact that I would have IO in places where it perhaps shouldn't be, the second option seems better, but I am new to all this and it may be that I'm missing something.
You can also give some of your actions the type Monad m => StateT GameState m (), so that parametricity guarantees no actual IO will occur. Stefan

1) Using State GameState r and then call execState for each game event (i.e. user input) so I can do IO 2) Using StateT GameState IO () and have the entire game live in one big execStateT call. (I note XMonad does something similar.)
I'm also interested in the answer to this question. One concern I would have about option 2 is that it looks like it "breaks encapsulation", to use a phrase from OOP. What I mean is, it seems like good design would mean that you could write and test the game logic totally independently of any IO. Game functions such as "makeMove" ought to have type signatures that don't involve any IO. Can this be achieved in option 2?

On 5/1/07, DavidA
1) Using State GameState r and then call execState for each game event (i.e. user input) so I can do IO 2) Using StateT GameState IO () and have the entire game live in one big execStateT call. (I note XMonad does something similar.)
I'm also interested in the answer to this question. One concern I would have about option 2 is that it looks like it "breaks encapsulation", to use a phrase from OOP.
What I mean is, it seems like good design would mean that you could write and test the game logic totally independently of any IO. Game functions such as "makeMove" ought to have type signatures that don't involve any IO. Can this be achieved in option 2?
You could, of course, do both. I do think it's a good idea to write your own "game monad" which encapsulates all of the unsafe functions that you might want to do in an application specific approprate way, rather than just having your "unsafe skin" be the IO monad. So for example, if you want to spawn an actor in the game world and attach an AI thread to govern its behaviour you'd need a function which interanlly uses forkIO, sets up a bunch of TVars for messaging etc., but from the Game monad's pespective that can be encapsulated. There *are* things which are just inherently imperative in nature, and are easier to do like that (threads with message passing, for example, is not necessarily bad, sometimes they are the Right Abstraction -- actor AI is probably a good example), but you can make it a lot nicer and less unsafe by writing your own "unsafe skin" which supplies slightly safer encapsulations tailored for your specific application. But of course, you could have another monad which ONLY deals with pure changes to the game state that you then evaluate from the underlying monad. So the GameEngine monad is the "unsafe skin", and then you have the Game monad which just does a pure computation on the game state. Then you'd have a function like: game :: Game a -> GameEngine a game g = do state <- get gstate <- gameState state let ( res, gstate' ) = runState g put ( state{ gameState = gstate' } ) return res Which simply runs a game action on the game part of the GameEngine state. -- Sebastian Sylvan +44(0)7857-300802 UIN: 44640862

DavidA wrote:
What I mean is, it seems like good design would mean that you could write and test the game logic totally independently of any IO. Game functions such as "makeMove" ought to have type signatures that don't involve any IO. Can this be achieved in option 2?
Here is one way: For functions that do not need IO, use types that look like: MonadState GameState m => ... -> m a For functions that only do IO without refering to the game state, use: MonadIO m => ... -> m a For functions that do both, use: (MonadIO m, MonadState GameState m) => ... -> m a Testing of the pure game functions can use State GameState, testing of pure IO functions can use IO, and production can use StateT GameState IO. -Yitz

On Mon, 30 Apr 2007, Denis Volk wrote:
Hello all,
I am trying to make a (turn-based) game in Haskell and need to pass around quite a bit of information, so using the State monad seems most appropriate. My question is, which is a better idea:
The famous "Why functional programming matters" contains an example for game programming. In this paper the complete tree of all possible games is constructed lazily, but only selected branches are visited. http://www.math.chalmers.se/~rjmh/Papers/whyfp.html

"Denis Volk"
I am trying to make a (turn-based) game in Haskell and need to pass around quite a bit of information, so using the State monad seems most appropriate. My question is, which is a better idea:
Have you read `Theseus and the Zipper'[1] yet? 1. http://en.wikibooks.org/wiki/Haskell/Zippers -vvv
participants (8)
-
DavidA
-
Denis Volk
-
Henning Thielemann
-
Isaac Dupree
-
Sebastian Sylvan
-
Stefan O'Rear
-
vvv@umc.com.ua
-
Yitzchak Gale