Original Post
I've been learning some Haskell this summer so I decided to write Pong as an exercise.
The source code/binaries are here: HPong (Windows) | HPong (Linux) These are some thoughts/problems that occured to me in the process in the hope that some of the Haskell people lurking around here will see this thread and address: 1. Callbacks require modifying state in some manner in order to let the normal program flow know something has happened. For Pong I used IORefs to modify a "KeysState" structure for when the user presses a key. It seems like we'd want to minimize the amount of state changing we have to do so this seemed like the best route rather than try and modify the world directly. 2. In the HPong source in the gameMain function I have three functions in a row that need to update the ball velocity variable (if the ball went out of bounds, if we hit a wall/paddle, if it needs to be reset). If I were using a "normal" language that allowed variable modification I would just update the ballVelocity variable in each function, but in Haskell each time I return a new ball velocity variable I have to give it a new variable name, e.g. ballVel, ballVel', ballVel'', etc. Is there a better, more functional perhaps way of doing this? 3. Dealing with random variables seems kind of annoying (particularly for games) since it violates referential transparency. This means any time I need a random number for the result to be useful the return value has to propogate upwards through do blocks until it reaches its destination. On second thought we could probably store the IO result and retain purity until we actually need to evaluate the result. I haven't fully thought this through yet. 4. The approach I took for the "gameMain" function seems fairly straightforward in that we can call whatever operations need to be done and pass around whatever pieces of the world state we need, then encapsulate all the game/world state pieces in a more-or-less monolithic data type, which gets passed back into gameMain when we tail recurse into the next iteration of the gameMain loop. Then in the new iteration we can extract whatever pieces we need from the monolithic "GameState" structure and repeat. At this highest level gameMain function though, it is basically imperative since I spend the entire time sitting in a do block (for the HPong source at least). The functions I'm calling are almost all pure themselves however. I'm wondering whether or not a game main function has to be imperative like this inherently, since the order is rather important (e.g. first check player inputs, then check collisions, then update A.I., finally draw everything). So, hopefully that's not too long winded. There's still a lot I need to learn (in particular monads), but I'd like to see what this discussion brings up.
The source code/binaries are here: HPong (Windows) | HPong (Linux) These are some thoughts/problems that occured to me in the process in the hope that some of the Haskell people lurking around here will see this thread and address: 1. Callbacks require modifying state in some manner in order to let the normal program flow know something has happened. For Pong I used IORefs to modify a "KeysState" structure for when the user presses a key. It seems like we'd want to minimize the amount of state changing we have to do so this seemed like the best route rather than try and modify the world directly. 2. In the HPong source in the gameMain function I have three functions in a row that need to update the ball velocity variable (if the ball went out of bounds, if we hit a wall/paddle, if it needs to be reset). If I were using a "normal" language that allowed variable modification I would just update the ballVelocity variable in each function, but in Haskell each time I return a new ball velocity variable I have to give it a new variable name, e.g. ballVel, ballVel', ballVel'', etc. Is there a better, more functional perhaps way of doing this? 3. Dealing with random variables seems kind of annoying (particularly for games) since it violates referential transparency. This means any time I need a random number for the result to be useful the return value has to propogate upwards through do blocks until it reaches its destination. On second thought we could probably store the IO result and retain purity until we actually need to evaluate the result. I haven't fully thought this through yet. 4. The approach I took for the "gameMain" function seems fairly straightforward in that we can call whatever operations need to be done and pass around whatever pieces of the world state we need, then encapsulate all the game/world state pieces in a more-or-less monolithic data type, which gets passed back into gameMain when we tail recurse into the next iteration of the gameMain loop. Then in the new iteration we can extract whatever pieces we need from the monolithic "GameState" structure and repeat. At this highest level gameMain function though, it is basically imperative since I spend the entire time sitting in a do block (for the HPong source at least). The functions I'm calling are almost all pure themselves however. I'm wondering whether or not a game main function has to be imperative like this inherently, since the order is rather important (e.g. first check player inputs, then check collisions, then update A.I., finally draw everything). So, hopefully that's not too long winded. There's still a lot I need to learn (in particular monads), but I'd like to see what this discussion brings up.