user1815463
user1815463

Reputation: 23

How to make a Racket macro take an integer argument?

I've been trying to make a macro for 'wrapping' functions, i.e. if I have a function that will take a list and cons the symbol 'a to the first element, it'd normally be defined as lambda (l) (cons 'a l), but I want a macro that will take a function and a list of integer-expression pairs and 'wrap' that function by making a new function that takes the some arguments of the old function and gets the rest based on the pairs, using the integer as a position and the expression as the value to give the function. Something like this:

(wrap list (0 'blah) (2 'bloo) (4 'blee))

Should expand to:

(lambda (a1 a2 . rest)
  (apply list 'blah a1 'bloo a2 'blee rest))

Problem is, I don't know how to get the macro to find the value of the integer in the pair, it only knows it as a syntax object. I'm fairly new to macros and this should be fairly simple, I'm just having trouble with the docs, and I can't find any info on the web other than simple tutorials on macros. Any help would be appreciated.

Upvotes: 0

Views: 385

Answers (1)

C. K. Young
C. K. Young

Reputation: 222973

This seems like a crazy macro to want to write, especially when SRFI 26 is so much more intuitive to use. Instead of (wrap list (0 'blah) (2 'bloo) (4 'blee)), you can just use:

(cut list 'blah <> 'bloo <> 'blee <...>)

That is surely much easier to read.


If you really must write such a macro, here's one way to go about it, by transforming usages of wrap into equivalent usages of cut:

(require (for-syntax syntax/parse) srfi/26)
(define-syntax (wrap stx)
  (syntax-parse stx
    ((_ func:expr (idx:nat expr:expr) ...)
     (let* ((alist (map cons
                        (syntax->datum #'(idx ...))
                        (syntax-e #'(expr ...))))
            (upper (add1 (apply max -1 (map car alist))))
            (listfunc (lambda (i)
                        (cond ((assv i alist) => cdr)
                              (else #'<>)))))
       (with-syntax (((args ...) (build-list upper listfunc)))
         #'(cut func args ... <...>))))))

The key to answering your question, about how to get the integers given the syntax objects, is by using syntax->datum (for deep syntax-stripping) or syntax-e (for shallow syntax-stripping).


(P.S. (To Racket experts reading this.) I'm very new to syntax-parse, so I'm sure syntax-parse experts can find better ways to write the above. I originally wrote the macro as a syntax-case macro, then slapped on syntax-parse syntax classes. That's it.)

Upvotes: 3

Related Questions