[mercury-users] lisp macros

Richard A. O'Keefe ok at cs.otago.ac.nz
Fri May 24 10:57:50 AEST 2002


	The one unique lisp feature seems to be macros; does anyone have
	much experience with lisp macros and what they add compared to
	programming in a language like Mercury?
	
There are two ideas of what "Lisp macros" might be.

There are the "hackers' macros" in Lisp, which give you the
power to make *arbitrary* source-to-source transformations.
They have been used to do amazing things, such as the "Series" package
(described at the back of CLtL2).

There are the "hygienic macros" in R5RS Scheme,
which give you rewrite rule macros that preserve lexical scope.
The standard examples show how most of the Scheme special forms
can be defined using these macros.  They are also used to define
object-oriented languages atop Scheme.

The nearest equivalent in functional programming would be the heavy use
that Haskell experts make of "combinators".  The goal is the same: to
let people write simple surface forms with possibly complex "plumbing"
underneath.  The equivalent in Prolog is term_expansion/2; there is an
equivalent in Erlang where a module can tell the compiler to run it
through a specified preprocessor before compilation proper.

Higher-order functions let you do many of the things that macros do,
but one thing they do not let you do is create new binding forms.

Here's a little example from some XML processing I did in Scheme using
unhygienic macros:

(defmacro x-define (Id . Body)
  `(define (,Id Element Ancestors) ,(list 'quasiquote Body)))
(defmacro apply-templates ()
  `(process-children Element Ancestors))

...

(x-define x-abstract
  (div (id . "abstract") #\newline
    (h2 (a (name . "abstract") "Abstract"))
    ,@(apply-templates)))


This definition expands to

    (define (x-abstract Element Ancestors)
      `(div (id . "abstract") #\newline
         (h2 (a (name . "abstract") "Abstract"))
         ,@(process-children Element Ancestors)))

which in turn expands (` is a macro) to

    (define (x-abstract Element Ancestors)
      (cons 'div
	(cons '(id . "abstract")
	  (cons '#\newline
	    (cons '(h2 (a (name . "abstract") "Abstract"))
              (process-children Element Ancestors))))))

It may not seem like a big deal, but this pattern is repeated dozens
of times in the program in question, and while Element and Ancestor
are needed by a few functions, the vast majority just have to pass
them on.  Passing them on explicitly would make the code complicated
and error-prone.  Hiding them makes it much easier to write correct
code.

With the aid of this kind of thing, you wouldn't _believe_ how much
smaller a Scheme program to transform XML can be than the corresponding
XSL program, nor how much clearer it can be.

For another example, suppose we wanted to imitate AWK in Scheme.
Hash tables and regular expressions are not built into Scheme, but
they are available in SLIB, so no worries there.  One of the key points
of AWK is that it hides all the file-reading details.  OK, we could do

    (awk <expression that yields a list of file names>
      <action>
      (if <test> <action>)
      (if (flip-flop <test> <test>) <action>)
      ...)

(defmacro (awk Files . Body)
  `(awk-function Files
    (lambda (File-Name Line-Number Line)
      (call-with-current-continuation
        (lambda (Next)
          , at Body)))))

and then

    (awk (cdr *argv*)
      (if (string=? Line "foo")
	  (displayln File-Name ":" Line-Number)))

would expand to

    (awk-function *argv*
      (lambda (File-name Line-Number Line)
        (call-with-current-continuation
          (lambda (Next)
	    (if (string=? Line "foo")
	      (displayln File-Name ":" Line-Number))))))

Here I'm assuming
  (define (displayln . args)
    (for-each display args)
    (newline))
which of course doesn't need to be a macro.

With this macro, "(awk ...) is a binding form for File-Name,
Line-Number, Line, and Next (quit current line, proceed to next).
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list