Python syntax in Lisp and Scheme (macro tangent)

Marco Baringer mb at bese.it
Mon Oct 6 03:22:03 EDT 2003


gregm at cs.uwa.edu.au writes:

> Really? Turing-completeness and all that... I presume you mean "cannot
> so easily be added as functions", but even that would surprise me.

well you can pass around code full of lambdas so most macros (expect
the ones which perform hairy source transformations) can be rewritten
as functions, but that isn't the point. Macros are about saying what
you mean in terms that makes sense for your particular app.

> : Here's another example of what you can do with macros in Lisp:
>
> : (with-collector collect
> :   (do-file-lines (l some-file-name)
> :     (if (some-property l) (collect l))))
>
> : This returns a list of all the lines in a file that have some property. 
>
> OK, that's _definitely_ just a filter: filter someproperty somefilename
> Perhaps throw in a fold if you are trying to abstract "collect".

no it's not, and the proof is that it wasn't written as a filter. For
whatever reason the author of that snippet decided that the code
should be written with WITH-COLLECTOR and not as a filter, some
languages give you this option, some don't, some people think this is
a good thing, some don't.

> : DO-FILE-LINES and WITH-COLLECTOR are macros, and they can't be implemented
> : any other way because they take variable names and code as arguments.
>
> What does it mean to take a variable-name as an argument? How is that
> different to taking a pointer? What does it mean to take "code" as an
> argument? Is that different to taking a function as an argument?

You are confusing the times at which things happen. A macro is
expanded at compile time, there is no such thing as a pointer as far
as macros are concerned (more or less), macros are passed pieces of
source code in the form of lists and atoms and return _source code_ in
the form of lists and atoms. The source code is then compiled (whith
further macro expansion in need be) and finally, after the macro has
long since finished working, the code is executed.

Another trivial example:

We often see code like this:

(let ((var (foo)))
  (if var
      (do-stuff-with-var)
      (do-other-stuff)))

So write a macro called IF-BIND which allows you to write this instead:

(if-bind var (foo)
  (do-stuff-with-var)
  (do-other-stuff))

The definition for IF-BIND is simply:

(defmacro if-bind (var condition then &optional else)
  `(let ((,var ,condition))
     (if ,then ,else)))

But what if the condition form returns multiple values which we didn't
want to throw away? Well easy enough:

(defmacro if-bind (var condition then &optional else)
  (etypecase var
    (cons `(multiple-value-bind ,var ,condition
             (if ,(car var) ,then ,else)))
    (symbol `(let ((,var ,condition)) 
               (if ,var ,then ,else)))))

Notice how we use lisp to inspect the original code and decide what
code to produce depending on whether VAR is a cons or a symbol.

I could get the same effect (from an execution stand point) of if-bind
without the macro, but the source code is very different. Macros allow
me to say what I _mean_, not what the compiler wants.

If you want more examples look in Paul Graham's OnLisp
(http://www.paulgraham.com/onlisp.html) book for the chapters on
continuations or multitasking.

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen




More information about the Python-list mailing list