[Haskell-cafe] Re: 'Proper' use of the State monad

Sebastian Sylvan sebastian.sylvan at gmail.com
Tue May 1 07:21:30 EDT 2007


On 5/1/07, DavidA <polyomino at f2s.com> wrote:
>
> > 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20070501/7a497e81/attachment.htm


More information about the Haskell-Cafe mailing list