
"Eli Ford"
Is there a better way than IORefs
The state Monad.
Do you mean one state shared among all actors, like this?
type MGame = State GameState newtype GameState = GameState { <shared state> }
That gets part of the way, but I'm thinking of a situation where each instance of a particular type of actor can have its own reference to some other actor:
newtype Watcher = Watcher { otherActor :: IORef ActorWrapper }
(Here, ActorWrapper is an existential type that hides a specific actor type.)
I started writing a game using IORefs that way, allowing actors to see and cause changes in each other, but in the back of my mind I wonder if this is a symptom of a C++ background.
As a general rule, if you're thinking IORef's you aren't thinking Haskell. You shouldn't think of a global state Monad as state being shared between all actors, but capturing the state of many actors at a particular instance of time: The state Monad, by itself, encapsulates time, not data. The data encapsulation is done by the type of the state, something like MyState = MyState { actors::(Map UKey Actor), ... } where UKey is unique for the whole run-time of the game, that is, if you store e.g. a reference to a door in the state of a switch, you can make the switch explode (either in the frame-global update or when triggered) if Mario happened to jump on the door and thus destroyed it. (Please don't take this as an example of good game design) If you look at the source of MonadState, you'll notice that it doesn't care about what you put into it, at all. It's merely pure imperative sugar that allows you to pretend not to be programming in a functional language. My model, of course, doesn't really differ from yours except that it can't blow up that badly as eg. you can't ever modify your game state from inside draw or mess with gl in update. As a general rule, always try hard to restrict usage of the IO Monad to the main function. It doesn't mean that you can't code imperatively and side-effect full in the rest of your program, it just means that you can't launch nuclear missiles and cause major side effects outside of your program. Nothing is stopping you from further restricting side-effects by using the state Monad on two different types to eg. seperate the GUI state from the game state, so that you can't ever select a menu item when Mario collects a mushroom or similar insanities. Decide for yourself how much granularity you want. One of the strangest features of Haskell is that, despite and because of being an uttermost functional language, it actually promotes imperative programming in the guise of monadic programming. -- (c) this sig last receiving data processing entity. Inspect headers for copyright history. All rights reserved. Copying, hiring, renting, performance and/or quoting of this signature prohibited.