Wednesday, April 27, 2005

If I were teaching macros

Yeah, if I were teaching macros I'd start without pattern matching. I would start with a programmed macro system like the one described by Dybvig et al, just without mentioning syntax-case. That way you'd have properly lexically scoped macros. And I wouldn't tell the students about datum->syntax-object so that they couldn't break hygiene. Here are some chapters in this hypothetical tutorial:

Lesson 1: Metaprogramming: programs that write programs.
Learn how to create syntax objects to create quoted code, by analogy to quote; talk about the phase distinction; write some simple macros that generate programs; make (over-)simplified analogy to cutting and pasting code.
Lesson 2: Macros are embedded metaprograms.
Demonstrate that macros can refer to identifiers that are in scope. Show that there's no danger of such identifiers getting captured when macros are referentially transparent; refine the idea of cutting and pasting code to being more than just textual replacement -- a variable reference identifies a binding and the generated code reproduces that binding, not necessarily the name.
Lesson 3: Macros can introduce identifiers.
Demonstrate how macros can introduce brand new identifiers; show that in hygienic macro systems those identifiers can never refer to existing bindings in any program, only to uses of the same identifier within a macro's templates; show how identifiers given as parameters can generate binding occurrences, bound occurrences, and quoted occurrences of the identifier.
If I could get through all of that without pattern matching, that'd be cool, but somewhere along the line the code might just get too hairy without it. (Then again, Lisp defmacro hackers live without it, don't they?) Since pattern matching is orthogonal to the above lessons, I would love to put it off until after mastering metaprogramming, the phase separation, and issues of scope.

Some might claim you can write macros in a hygienic macro system without understanding properly lexically scoped macros, since they're just taken care of for you. That's pretty much doomed to failure, because as a macro writer you need to understand the distinction between names and identifiers in order to know when you can and can't bind a particular identifier.

4 comments:

Sam TH said...

Really? How will you introduce names to be used by (syntax)? If you don't use the syntax form, then you'll have to rely on the pun between lists of syntax objects and syntax objects of lists, so that you can implement sequence as an alias of begin like this:

(define-syntax (seq stx)
(let ((rest (syntax-cdr stx)))
`(,#'begin ,rest)))

You can't really tell me that's clearer than pattern-matching.

Also, you claim that you wouldn't introduce reference to lexically-bound identifiers until lesson 2. But the macro above has a reference to begin. What macros will you start with?

Dave Herman said...

If you don't use the syntax form...

Um, I will use the syntax form. I just won't be using syntax-case or syntax-rules. In other words, I will use templates, I just won't use pattern matching. The point is that for people who've never written programs with pattern matching, that's a part of the learning curve. And it's orthogonal to the other pedagogical issues I mentioned.

You can't really tell me that's clearer than pattern-matching.

It's not. Nor did I claim it is. In fact, I linked to my earlier post explaining why pattern-matching is better. The point is not what's a better technology, it's what's a better pedagogical approach. Language levels, yo.

But the macro above has a reference to begin.

There's no need to be absolutist. In fact, most novices won't notice that "primitives" like begin and lambda are actually scoped. So I'm not too worried about that confusing them. The point is that I wouldn't discuss referential transparency until lesson 2, and would try to choose examples that don't feature it too prominently.

I was talking about pedagogy, not technology.

Felix said...

I'm not convinced either.

I think I would need to see the first lesson before I'd understand how this leads to better pedagogy.

Felix said...

Ah, I think I know why I have doubts.

Its because I believe that ML and Haskell got it right. Pattern matching is the way that dereferencing the components of a structure should be taught. If you want selectors, you can implement them yourself as factored out match expressions.

[The problem with the above belief is that it is flawed. There isn't a standard solution for handling abstract data types with match. So that's one win for programming with selectors instead of matching on the concrete structure. (And this objection may apply in the domain of macro programming. Syntax seems like abstract data type: it has extra state like the source information and other syntax properties.)]