[racket] Unquote symbol outside quasiquote
On 09/04/2012 03:30 PM, André Matheus wrote:
> Hi everyone,
>
> I want to create a macro to get fields of an object specified by a list
> of symbols. Like
>
> (get-fields '(x y) obj) -> (list (get-field x obj) (get-field y obj))
>
> Now, my macro will receive (quote x) and (quote y), for example. What I
> want to know
> is, what is the right way to unquote it? Using eval as in
>
> (datum->syntax #'lex `(get-field ,(eval (second (syntax->datum
> #'(get-fields 'y xx)))) xx) #'srcloc)
>
> removes the quote but I don't think it's the right way to do it.
It's possible to do what you described, but it's "un-Rackety". It would
be more idiomatic to design the syntax so that 'get-fields' takes a
parenthesized sequence of identifiers (*not* an expression), like this:
;; eg (get-fields (x y) obj)
(define-syntax-rule (get-fields (field-id ...) obj-expr)
(let ([v obj-expr])
(list (get-field field-id v) ...)))
On the other hand, that means that if you want to compute the list of
fields, you have to do that through another macro that expands into a
use of 'get-fields'. For example:
(define-syntax-rule (get-fields-plus-x-and-y (field-id ...) obj-expr)
(get-fields (field-id ... x y) obj-expr))
If you do really want the macro to accept the fields as a *compile-time
expression*, use 'syntax-local-eval' to evaluate the expression:
(require (for-syntax racket/syntax))
(define-syntax (get-fields stx)
(syntax-case stx ()
[(get-fields fields-ct-expr obj-expr)
(with-syntax ([(field-id ...)
(syntax-local-eval #'fields-ct-expr)])
#'(let ([v obj-expr])
(list (get-field field-id v) ...)))]))
Using 'eval' would evaluate the expression in the wrong environment,
although for simple expressions it can be hard to tell. Here's a test
case that shows the difference:
(define a%
(class object%
(super-new)
(init-field x y z)))
(define a (new a% (x 1) (y 2) (z 3)))
(require racket/stxparam)
(define-syntax-parameter the-fields '(x))
(require rackunit)
(check-equal?
(get-fields (syntax-parameter-value #'the-fields) a)
'(1))
(check-equal?
(syntax-parameterize ((the-fields '(y z)))
(get-fields (syntax-parameter-value #'the-fields) a))
'(2 3))
Ryan