2

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.

Coderino Javarino
  • 2,819
  • 4
  • 21
  • 43
  • Are the types all just ignored? By that I mean, is `(arg type)` equivalent to just `arg`? If not, is there a type you can pair each arg with such that `arg` is equivalent to `(arg type)`? – Alex Knauth Jun 20 '20 at 23:22
  • @AlexKnauth no, types are not ignored. The macro expands to begin form that has more going in it, but the expansions into define is the only part giving me trouble. Yes, there is a default type, and arg would be equivalent to (arg any) – Coderino Javarino Jun 20 '20 at 23:25
  • Okay. Then there should be a way to transform every `arg` into `(arg any)`, and then turn it into the define once it's all consistent. – Alex Knauth Jun 20 '20 at 23:37
  • yes, but how can I do it automatically as part of `define/custom` macro? – Coderino Javarino Jun 20 '20 at 23:40

1 Answers1

2

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 ...))))
Alex Knauth
  • 8,133
  • 2
  • 16
  • 31