On Dec 24, 2008, at 8:11 AM, Chouser wrote:
On Wed, Dec 24, 2008 at 1:44 AM, Stephen C. Gilardi <squee...@mac.com> wrote:Based on Chouser's version, but using atoms, a splash of color, a littlemore destructuring and more separation of model and view.Would you be willing to explain why you think it's correct to use atoms here? I considered it, but wasn't sure it was right. There are two mutable objects, after all -- do they not need to be coordinated?
I actually hoped it would spark some discussion about atoms in view of recent threads that featured warnings about using atoms only very rarely. Here are my thoughts:
- Even in a simple game like this, it's important that the concurrency be handled in a correct way. (We don't want the snake flying apart over time, for example.) Persistent data structures handle most of that. Proper handling of the remaining mutable references provides the rest.
- I'm assuming that keyPressed, actionPerformed, and paintComponent are completely asynchronous with each other. Swing may make stronger promises, but I'm assuming the worst. I do assume that multiple calls to actionPerformed don't overlap each other since I know they happen at 75 ms intervals.
- Regarding keyPressed, in the case of a race with actionPerformed, the snake either "moves then turns" or "turns then moves". Those two orderings give results that are macroscopically equivalent in this game--the behavior will appear correct to the player either way.
- Regarding paintComponent- atomic update and the fact that we (now) only dereference each of apple and snake once ensure that we will paint a consistent view of each. The former code dereferenced apple twice, once for each of its x and y coordinates--shouldn't that have been inside a dosync? The code using atoms dereferences apple once for both coordinates. The new paint function provided a natural way to do that: at argument evaluation time. - in a race with actionPerformed, the apple might, for one call to paint, appear at its new location with an ungrown snake. We could come up with a "rules of the world" spec with promises about what the user could ever see that would be violated by this, but I don't see how it can interfere with game play. It does heal itself correctly and is unlikely to be noticed by a player. I believe that preventing this momentary, purely visual skew is the only coordination that the dosync would provide in this case.
Using dosync over atoms here would allow the programmer to avoid much of the thought and analysis above. If one were to apply the rule "if there is more than one mutable object involved, use dosync to coordinate updates", the analysis would be much simpler and much less likely to give any surprises. I think it's a good way to go.
Great use of :grow instead of 'true' when calling 'move'. So sharp, Stephen!
:-) Mysterious flag values of "true" have always bothered me in other languages. I like the code to speak for itself as much as possible. I'm glad you liked it!
--Steve
smime.p7s
Description: S/MIME cryptographic signature