Lara
Lara

Reputation: 3164

Mapping within macro without extra parentheses?

Say I have a macro like this:

(define-syntax (choose stx)
  (define data (syntax->datum stx))
  (define args (cadr data))
  (define body (cddr data))
  (define output
    `(apply (case (car ,args)
              ,(map (lambda (choice)
                           `((,(car choice)) ,(cadr choice)))
                         body)
              (else (displayln "error")))
            (cdr ,args)))
  (println output)
  #'(void))

If I use this on something like this (there could be more options):

(choose args
        ("run" runsomething)
        ("del" delsomethingelse))

It transforms it to

(apply
 (case (car args)
   ((("run") runsomething)
    (("del") delsomethingelse))
   (else (displayln "error")))
 (cdr args))

Which is not valid code, because the map gave it extra parentheses. Instead I want it to give me this:

(apply
 (case (car args)
   (("run") runsomething)
   (("del") delsomethingelse)
   (else (displayln "error")))
 (cdr args))

How could I do something like this?

Upvotes: 1

Views: 73

Answers (1)

soegaard
soegaard

Reputation: 31147

Use unquote-splicing (aka ,@) to get rid of the list surrounding map.

Example:

(define xs '(a b c))
`(1 2 ,xs  3 4)    ; => '(1 2 (a b c) 3 4)
`(1 2 ,@xs 3 4)    ; => '(1 2 a b c 3 4)

However I notice that you use syntax->datum on the input stx of the syntax transformer. That removes lexical information, which could end up causing problems. It recommend using either syntax-case or syntax-parse, which use pattern matching to pick out the elements of the input syntax and templates to generate the output.

(define-syntax (choose stx)
  (syntax-case stx ()
    [(_choose args
              (datum fun-expr)
              ...)
     #'(apply (case (car args)
                [(datum) fun-expr]
                ...)
              (cdr args))]))

(define (run-it . xs) (list 'ran-it  xs))
(define (del-it . xs) (list 'delt-it xs))

(choose (list "run" 1 2 3)
        ("run" run-it)
        ("del" del-it))

Output: '(ran-it (1 2 3))

Upvotes: 2

Related Questions