> Well, while syntax-rules macros are quite easy to understand
> (at least from the user's point of view), although sometimes a littleIf you, or the other people who are confused by syntax-case, can point
> tricky, the syntax-case system I find still too difficult to use.
> define-macro, on the other hand, is very easy to explain
> even to beginner programmers, although the resulting macros
> are much more difficult to analyse.
to the parts of the manual that confuse you, so we can clear them up, I
think we'd all appreciate it.
The order of presentation in the manual is that syntax-rules macrosare explained first, because they are more limited, and there's noneed to get into details regarding syntax objects.I used to explain the "syntax-rules" macros to my friends byfirst showing "what we want to transform into what", and then packingit up in some magic to work (because basically that's how Iunderstand them). The advantage was that the meaning of the macrois readily visible and there's no need to analyse the code.The manual however makes very little reference to define-macro systemto explain how syntax-case works. It is easy to see that lisp programsare lists of symbols, and so they can be processed like any otherlists of symbols before they are evaluated -- that's the essentialsof lisp macros. In case of "syntax-case" some new mysteriousnotions appear: namely, the unfamous syntax objects. And while itis easy to imagine how the list of symbols look like, all we readabout in the manual is that "the syntax expander representsidentifiers as annotated syntax objects, attaching such informationto those syntax objects as is needed to maintain referentialtransparency". We don't know what sort of information is that, andwhy is it better to use syntax-case over define-macro.Especially if we compare the "anamorphic if" definition from themanual,(define-syntax aif(lambda (x)(syntax-case x ()((_ test then else)(syntax-case (datum->syntax x 'it) ()(it#'(let ((it test))(if it then else))))))))with the most straightforward "anamorphic if" with define-macro:(define-macro (aif test then else)`(let ((it ,test))(if it,then,else)))(Even if the latter is not exactly right, as is argued in section6.10.5, it generally does its job)Also, I have to admit that I still don't understand what thesyntax-case macro above is doing. Furthermore, it isn't clear whysyntax->datum takes only one argument, and datum->syntax takestwo (a syntax object and the "datum" itself)Fundamentally, syntax-case shouldn't be harder to use than define-macro
99% of the time, if you remember
- macros are functions from "syntax-objects" to syntax-objects
- syntax-objects are smart symbols
- syntax->datum to remove the smartness
- datum->syntax is for when you want to break hygiene (but syntax
parameters are better where applicable)
- use quasisyntax to construct lists of syntax-objects instead of
quasiquote to construct lists of symbols.
Maybe showing a sufficient number of examples would bemore helpful, because I haven't got a clue "where syntaxparameters are applicable" or how I could use quasisyntaxand unsyntax.By the way, I've had another problem when defining a macro.I've been trying to implement something like dynamical scoping,but such that it wouldn't require introducing global variablesThe idea is that one can write
(with-default ((a 5))(define (f x) (+ x (specific a))))and that later the procedure could be used like that:(f 10)===> 15(specify ((a 20))(f 10))===> 30The first attempt was that there could be a global hash table*specifics* that would store the lists of values, and that"specific" could be a local macro that would expand to a hashreference. So I tried to implement that using syntax-rulesin the following way:(define-syntax with-default(syntax-rules ()((_ ((name value) ...)actions ...)(let-syntax ((specific (syntax-rules (name ...)((_ name)(hash-ref *specifics* 'name value));; don't mind shadowing for now...)))actions ...))))However, when I tried to use it, it seemed that the let-syntaxbehaves as if it wasn't there (so it didn't behave at all!):(with-default ((a 5))(define (f x)(+ x (specific a))))The situation was (again) resolved by creating a nasty combinationof "define-macro" and "syntax-rules", namely:
(define-macro (with-default bindings . actions)(match bindings(((names values) ...)`(let-syntax ((specific (syntax-rules ,names,@(map (match-lambda((name value)`((_ ,name)(hash-ref *defaults*',name ,value))))bindings)))),@actions))))It's not that I like it. I really don't. But I don't know how howelse this could be achieved, nor why the former solutionso stubbornly resists to work.Thanks,M.