[racket] Unquote symbol outside quasiquote

From: Ryan Culpepper (ryan at cs.utah.edu)
Date: Tue Sep 4 16:37:43 EDT 2012

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


Posted on the users mailing list.