[racket] Macros that expand to field and method definitions
#|An update: here is what the macro looks like now.|#
(define-syntax (define-property stx)
(define make-id
(lambda (template . ids)
(define str (apply format template (map syntax->datum ids)))
(datum->syntax stx (string->symbol str))))
(syntax-parse stx
[((~literal define-property) name:id init-value)
(with-syntax ([getter-name (make-id "get-~a" #'name)]
[setter-name (make-id "set-~a" #'name)])
#'(begin (define name init-value)
(define/public (setter-name value)
(set! name value))
(define/public (getter-name)
name)))]
[((~literal define-property) name:id init-value ((~literal
get:expr) get-body))
(with-syntax ([getter-name (make-id "get-~a" #'name)])
#'(begin (define name init-value)
(define/public (getter-name)
get-body)))]
[((~literal define-property) name:id init-value ((~literal
set) (setter-vars:id ...) set-body:expr))
(with-syntax ([setter-name (make-id "set-~a" #'name)])
#'(begin (define name init-value)
(define/public (setter-name setter-vars ...)
set-body)))]
[((~literal define-property) name:id init-value ((~literal
get) get-body) ((~literal set) (setter-bindings:id ...) set-body:expr))
(with-syntax ([getter-name (make-id "get-~a" #'name)]
[setter-name (make-id "set-~a" #'name)])
#'(begin (define name init-value)
(define/public (setter-name setter-bindings ...)
set-body)
(define/public (getter-name)
get-body)))]))
Somewhat like
http://msdn.microsoft.com/en-US/library/w86s7x04%28v=vs.80%29.aspx, but
with mandatory initialization expressions for each property.
-Patrick
On 25 April 2012 10:10, Asumu Takikawa <asumu at ccs.neu.edu> wrote:
> On 2012-04-25 03:19:46 -0400, Patrick Mahoney wrote:
> > I'm looking to define a macro that extends the forms within a
> racket/class
> > class%. In particular, I would like a form to define a C# like class
> > language form property, that implements the getters and setters for an
> > initialized field automatically.
>
> FYI, `get-field` and `set!-field` already allow you to get and set any
> public fields on an object.
>
> > (define-syntax (define-property syntax)
> > (syntax-parse syntax
> > [((~literal define-property) name:id init-value)
> >
> > #'(define name init-value)
> > (define/public (set-name! init-value)
> > (set! name init-value))
> > (define/public (get-name)
> > name)]))
>
> So this macro won't quite work as written. Here's an alternative that
> will at least run (but still won't work as you expect):
>
> (define-syntax (define-property stx)
> (syntax-parse stx
> [(_ name:id init-value)
> #'(begin (define name init-value)
> (define/public (set-name! new-val)
> (set! name new-val))
> (define/public (get-name) name))]))
>
> One issue was that you were using `syntax` as the argument to this
> transformer, which won't work because you end up shadowing
> `syntax`, which is needed to write the template. I changed it to `stx`.
>
> Secondly, you need to use `begin` to splice all of the definitions
> together.
>
> Note that this still doesn't work though. The `set-name!` and `get-name`
> will be introduced as is (or hygienically renamed) and won't use the
> name of the field as you might want. To do that, you might want to write
> `define-property` so that it takes get/set method names as arguments.[1]
>
> [1]: otherwise you need to unhygienically introduce a binding, which
> is best to avoid unless you know what you are doing.
>
> > define/public: use of a class keyword is not in a class top-level in:
> > (define/public (set-name! init-value) (set! name init-value))
>
> You probably got this error by trying out the macro outside of a class
> or because you weren't splicing the definition quite right.
>
> Cheers,
> Asumu
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20120425/b9f92406/attachment-0001.html>