[racket] world/universe update model (functional update redux), using the I/O monad for state

From: John Clements (clements at brinckerhoff.org)
Date: Mon Dec 12 14:52:56 EST 2011

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>

Posted on the users mailing list.