[racket] Advice: simulating stateful API in functional setting
Last Monday I attended Dan Garcia's workshop on BYOB. He kept pointing out (presumably for the benefit of Emmanuel and me) that it really is a functional language. And yes, it has "map", "filter", "foldl", and "lambda"... but most of the examples and assignments one normally does in BYOB use very stateful turtle graphics. So naturally I thought "Could I simulate this in universe?"
I defined a sprite struct (with an icon, a location, a direction, a pen, etc.) and a world struct (with a list of sprites and a picture), and wrote a bunch of low-level operations on these that would obviously be needed. So far so good. I'm imagining that student code might say something like
; zigzag : sprite -> sprite
(define (zigzag sprite)
(move 10 (rotate-cw 45 (move 10 (rotate-ccw sprite)))))
...
(define this-sprite (make-sprite (bitmap "sprite-icon.png") (make-posn 0 0) 0 ... zigzag)
...
(run-byob-world (list this-sprite that-sprite third-sprite) initial-background-picture)
which would in turn call big-bang with a prewritten draw handler and a tick handler that calls each sprite's action on it.
The problem is that some sprite actions (most obviously, "move" when you're in pen-down mode) affect not only the sprite itself but the world's picture. So far the least-bad solution I've come up with is to hide a mutation of that picture inside the otherwise-functional "move" function.
I've experimented with a couple of ways to do that. One way is to define (in my code, not visible to student code) a box named "world-pic", which is set-box!ed in "move" and "run-byob-world". Another is to use just a plain variable and "set!", again hidden inside "move" and "run-byob-world". Both seem to work for my toy examples so far; is there any advantage of one over the other? What subtle gotchas am I going to run into? Is "a little bit of mutation" like "a little bit pregnant?"
Stephen Bloch
sbloch at adelphi.edu