[racket] world/universe update model (functional update redux), using the I/O monad for state
The holiday season: time to grade lots of projects, and "goof off" by thinking about how those programs could better be expressed.
Here's a world/universe program written by one team in my class. It uses mutation all over the place:
#lang racket
(require (except-in (planet clements/rsound)
overlay
scale)
2htdp/universe
2htdp/image)
(define octave 48)
(define volume .4)
(define duration 22050)
(define (my-draw num)
(empty-scene 100 100))
(define (my-key world key)
(begin
(cond [(equal? key "0") (set! octave 0)]
[(equal? key "1") (set! octave 12)]
[(equal? key "2") (set! octave 24)]
[(equal? key "3") (set! octave 36)]
[(equal? key "4") (set! octave 48)]
[(equal? key "5") (set! octave 60)]
[(equal? key "6") (set! octave 72)]
[(equal? key "7") (set! octave 84)]
[(equal? key "8") (set! octave 96)]
[(or (equal? key "=") (equal? key "+")) (if (< volume 1) (set! volume (+ volume .1)) (set! volume volume))]
[(or (equal? key "-") (equal? key "_")) (if (> volume 0) (set! volume (- volume .1)) (set! volume volume))]
[(equal? key "z") (if (> duration 11025) (set! duration (- duration 11025)) (set! duration duration))]
[(equal? key "x") (if (< duration 88200) (set! duration (+ duration 11025)) (set! duration duration))]
[(equal? key "q") (play (make-tone (midi-note-num->pitch (+ 12 octave)) volume duration))]
[(equal? key "w") (play (make-tone (midi-note-num->pitch (+ 13 octave)) volume duration))]
[(equal? key "e") (play (make-tone (midi-note-num->pitch (+ 14 octave)) volume duration))]
[(equal? key "r") (play (make-tone (midi-note-num->pitch (+ 15 octave)) volume duration))]
[(equal? key "t") (play (make-tone (midi-note-num->pitch (+ 16 octave)) volume duration))]
[(equal? key "y") (play (make-tone (midi-note-num->pitch (+ 17 octave)) volume duration))]
[(equal? key "u") (play (make-tone (midi-note-num->pitch (+ 18 octave)) volume duration))]
[(equal? key "i") (play (make-tone (midi-note-num->pitch (+ 19 octave)) volume duration))]
[(equal? key "o") (play (make-tone (midi-note-num->pitch (+ 20 octave)) volume duration))]
[(equal? key "p") (play (make-tone (midi-note-num->pitch (+ 21 octave)) volume duration))]
[(equal? key "[") (play (make-tone (midi-note-num->pitch (+ 22 octave)) volume duration))]
[(equal? key "]") (play (make-tone (midi-note-num->pitch (+ 23 octave)) volume duration))]
[(equal? key "a") (play c-hi-hat-1)]
[(equal? key "s") (play c-hi-hat-2)]
[(equal? key "d") (play o-hi-hat)]
[(equal? key "f") (play snare)]
[(equal? key "g") (play bassdrum)]
[(equal? key "h") (play bassdrum)]
[(equal? key "j") (play bassdrum-synth)]
[(equal? key "k") (play clap-1)]
[(equal? key "l") (play clap-2)]
[(equal? key ";") (play crash-cymbal)]
[else #f])
0))
(big-bang 0
(to-draw my-draw)
(on-key my-key))
Standard functional update observation:
This is definitely the natural way to write the program: certain keypresses correspond to certain changes in the state of the world, and the rest stays the same.
New part?
What if a world function could produce a list of "state change instructions"; something like (list (change world-volume 54) (change octave 34)). The caller of world can interpret this state change as a functional update. I see this as having an advantage over functional update in that the world doesn't require an explicit reference, and there's no chaining for multiple updates.
Random connection:
It occurs to me that this is essentially using the I/O monad for state. That is, evaluation of the function gets a single world to work with, and is unable to update it, but can produce a list of changes to be applied when the function returns.
Thanks for letting me ramble,
John
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4624 bytes
Desc: not available
URL: <http://lists.racket-lang.org/users/archive/attachments/20111212/cb4ca4d2/attachment.p7s>