[racket] writing a define-world macro
Because it can get really annoying to have to retrofit a whole program
when you add a bit of extra state to a world, Barry Brown first
suggested and then I--having completely forgotten about Barry's
suggestion--re-suggested that worlds include functional update
functions, so that changing a single field in a world didn't require
students to include boilerplate code to leave all the other fields
unchanged.
I think the following macro just about does this:
#lang racket
(provide define-world)
(define-for-syntax (build-name id . parts)
(datum->syntax
id
(string->symbol
(apply string-append
(map (lambda (p)
(cond
[(syntax? p) (symbol->string (syntax-e p))]
[(symbol? p) (symbol->string p)]
[(string? p) p]))
parts)))
id))
(define-for-syntax (updater id fields field)
(let ([name (build-name id id '- 'update- field)]
[instance (build-name id 'a- id)]
[constructor (build-name id 'make- id)])
`(define (,name ,instance val)
(,constructor ,@(for/list ([f fields])
(if (equal? f field)
'val
`(,(build-name id id '- f) ,instance)))))))
(define-syntax (define-world stx)
(syntax-case stx ()
[(_ id fields)
(let ([field-names (syntax->list #'fields)])
#`(begin
(define-struct id fields)
#,@(for/list ([f field-names])
(updater #'id field-names f))))]))
How do I provide tests, and is there any chance something like this
could end up in 2htdp/universe?
Todd