3

I'm exploring Scheme macros, but I've been unable to find a portable way of writing anaphoric macros.

I'm trying to write an each-it macro, such that this code:

(each-it (list 1 2 3)
  (display it))

Expands to this:

(for-each (lambda (it)
            (display it))
          (list 1 2 3))

I've written a macro with syntax-rules, but this gives me an error about an undefined identifier when I try to use it.

(define-syntax each-it
  (syntax-rules ()
    ((each-it lst body)
     (for-each (lambda (it) body)
               lst))))

This SO question mentions define-syntax-parameter, which seems to be Racket only. This blog post gives some Scheme code samples, but the code samples don't run in DrRacket in R5RS mode (I think it's the square brackets?).

R4RS has an interesting macro appendix but it is not present in R5RS and I don't know if I can depend on it.

Can I write my each-it macro in a completely portable way? If not, what are the most widely available macro system features for writing my macro?

Community
  • 1
  • 1
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192

2 Answers2

4

This should be portable, at least in R6RS:

(define-syntax each-it
  (lambda (x)
    (syntax-case x ()
      ((_ lst body)
       (with-syntax ((it (datum->syntax x 'it)))
         #'(for-each (lambda (it) body) lst))))))
uselpa
  • 18,732
  • 2
  • 34
  • 52
  • The first argument to `datum->syntax` has to be an identifier, so putting `x` there is wrong. To correct it, replace `_` in the above code with `k` and replace the first argument to `datum->syntax` with `#'k`. – Marc Sep 14 '21 at 13:30
3

Yes, you can write it in a portable way assuming that R6RS is portable enough for you. (The same cannot be said on R7RS, which currently has nothing more than just syntax-rules, and it's unclear what will be included in the large language, or when it will happen.) See uselpa's for how to do that.

So why am I writing another answer? Because actually doing that is going to be a bad idea. A bad idea not in some vague academic sense that doesn't matter for most real world code -- bad in a sense that is likely to bite you later on. I know that "paper" makes it look intimidating, but read at least the first two sections of the paper mentioned in the other SO question you've seen. Specifically, Section 1.2 shows the problem you'll be running against. Then, Section 2 shows how to do it "properly", in a way that makes it tedious to write macros that expand to uses of your macro. At this point, it will be appealing to take the "just keep it hygienic", but at the end of Section 2 you'll see why that's not working either.

The bottom line, IMO, is to just not do it unless you have syntax parameters or something similar. Maybe the only exception to that (which might be your case) is when the macro is something that you intend to use yourself, and you will never provide it to others.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110