Let's say I have a macro (define/custom (name (arg type) ...) body ...)
that among other things expands to (define (name arg ...) body ...)
. That's easy.
Now, I want to allow not only (arg type)
to be passed as parameter, but simply arg
. Alright, so I write a second clause, where (define/custom (name arg ...) body ...)
is expanded to (define (name arg ...) body ...)
. Also easy.
But with such solution, either all arguments are with type, or none of them are. How can I allow mixing the two options in same syntax list (or whatever the ...
is called)? How can I make, so that eg. (define/custom (name arg1 (arg2 type2)) #f)
gets appropriately expanded to (define (name arg1 arg2) #f)
? The intuition is to use a helper macro, which would expand (helper a)
to a
, and (helper (a b))
to a
, and make (define/custom (name arg_or_arg+type ...) body ...)
expand to (define (name (helper arg_or_arg+type) ...) body ...)
, but as you probably knew and guesses where this is coming, this doesn't work, because define
expansion takes place before helper
expansion.
You can do this with a helper macro that loops through each "arg-or-arg+type" and transforms them into (arg type)
to be consistent.
First, I recommend defining a core version of the macro that only works on the consistent (arg type)
version of things:
(define-syntax define/custom-core
(syntax-rules ()
((_ (name (arg type) ...) body ...)
; among other things
(define (name arg ...) body ...))))
Then you can define helper macro that deals with 2 lists of input args: one for consistent (arg type)
things, and another for "arg-or-arg+type" things. Example usage might look like:
(define/custom-helper (name ((arg type) ...) (arg-or-arg+type ...)) body ...)
As it loops through arg-or-arg+type ...
, it will move them into the (arg type) ...
list. When arg-or-arg+type ...
is empty, it's done and puts all the (arg type)
things into a call to define/custom-core
.
(define-syntax define/custom-helper
(syntax-rules ()
((_ (name (arg+type ...) ()) body ...)
(define/custom-core (name arg+type ...) body ...))
((_ (name (arg+type ...) ((arg type) . rest)) body ...)
(define/custom-helper (name (arg+type ... (arg type)) rest) body ...))
((_ (name (arg+type ...) (arg . rest)) body ...)
(define/custom-helper (name (arg+type ... (arg any)) rest) body ...))))
This relies on arg
being equivalent to (arg any)
.
Then, all that's left is the outward-facing define/custom
macro to call the helper macro. It can pass an empty arg+type
list and pass the args into the arg-or-arg+type
place for the helper to deal with.
(define-syntax define/custom
(syntax-rules ()
((_ (name arg-or-arg+type ...) body ...)
(define/custom-helper (name () (arg-or-arg+type ...)) body ...))))