3 ms·
First I've heard of TXR. Very impressive!
by lisper 5y ago
First I've heard of TXR. Very impressive!
- deleted 5y ago[deleted]
- kazinator 5y agoI made an interesting discovery yesterday. When you allow a symbol to have both a function and macro binding, it behooves you to implement the following expansion rule: If a function call form (op ...) is the result of the expansion of a macro form (op ...) then after its argument forms are recursively expanded, if those recursive expansions make any difference, the resulting form should again be tried as a macro form. This is because transformations on the arguments of the function call may turn the call into something that is interesting to the macro again. With this, I can do the following now: This is the TXR Lisp interactive listener of TXR 275. Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet. TXR contains many small parts, unsuitable for children under 12 months. 1> (defmacro sqrt (:match :form f) (((* @exp @exp)) exp) ;; (sqrt (* x x)) -> x (@else f)) ** expr-1:1: warning: defmacro: defining sqrt, which is also a built-in defun sqrt 2> (defmacro expt (:match :form f) ((@exp 2) ^(* ,exp ,exp)) ;; (expt x 2) -> (* x x) (@else f)) ** expr-2:1: warning: defmacro: defining expt, which is also a built-in defun expt 3> (expand '(sqrt (expt x 2))) x See? (sqrt (expt x 2)) reduced down to x, even though the sqrt macro has no such optimization case. First (sqrt (expt x 2)) is tried as a macro. That immediately hits a fixed point by returning the form. The expander makes a note that expansion took place (even if a no-op). Then the expander expands it as a function call, expanding the argument expression, turning the form into (sqrt (* x x)). That expansion having made a difference, together with the fact that the form came from a macro, now means that the expander tries the form again as a macro: (sqrt (* x x)) is expanded again, and now there is a "hit". (For simplicity, these examples don't care about multiple evaluation; and it is a given that algebraic changes are risky in the face of floating-point. It's a good way to illustrate the technique.) CL compiler macros could do something like this. I don't see the requirement in the HyperSpec. The requirement could be that when a compiler macro declines expansion, then after the resulting function form is recursively processed for more macros, the compiler macro should be tried again, and this process should repeat until both conditions are met: the macro declines, and the function form no longer contains any arguments that are macros. Of course, a compiler macro can capture an &environment and do its own expanding via macroexpand. In this example, that would work. What I implemented is subtly different: a complete expansion is performed on the function. You would need macroexpand-all to simulate the feature in the macro itself. Not having to do that at all, having things Just Work, is pretty nice.
- lisper 5y agoYou need to start a blog. No one but me is ever likely to see this otherwise.