Python syntax in Lisp and Scheme

Alex Martelli aleax at aleax.it
Sat Oct 4 05:44:58 EDT 2003


Erann Gat wrote:
   ...
> But if you focus on examples like this you really miss the point.  Imagine
> that you wanted to be able to write this in Python:
> 
> def vector_fill(v, x):
>   for i from 0 to len(v)-1:
>     v[i] = x
> 
> You can't do it because Python doesn't support "for i from ... to ...",
> only "for i in ...".  What's more, you can't as a user change the language
> so that it does support "for i from ... to ...".  (That's why the xrange
> hack was invented.)

Almost right, except that xrange is a hack.  Since in Python you cannot
change the language to suit your whims, you USE the language (designed
by a pretty good language designer) -- by coding an iterator that is
suitable to put where the ... are in "for i in ...".

> In Lisp you can.  If Lisp didn't already have LOOP or DOTIMES as part of
> the standard you could add them yourself, and the way you do it is by
> writing a macro.

Good summary: if you fancy yourself as a language designer, go for Lisp;
if you prefer to use a language designed by somebody else, without you
_or any of the dozens of people working with you on the same project_
being able to CHANGE the language, go for Python.

> That's what macros are mainly good for, adding features to the langauge in
> ways that are absolutely impossible in any other language.  S-expression
> syntax is the feature that enables users to so this quickly and easily.

Doesn't Dylan do a pretty good job of giving essentially the same
semantics (including macros) without S-expression syntax?  That was
my impression, but I've never used Dylan in production.

> For example, imagine you want to be able to traverse a binary tree and do
> an operation on all of its leaves.  In Lisp you can write a macro that
> lets you write:
> 
> (doleaves (leaf tree) ...)
> 
> You can't do that in Python (or any other langauge).

Well, in Ruby, or Smalltalk, you would pass your preferred code block
to the call to the doleaves iterator, giving something like:

    doleaves(tree) do |leaf|
        ...
    end

while in Python, where iterators are "the other way around" (they
get relevant items out rather than taking a code block in), it would be:

    for leaf in doleaves(tree):
        ...

In either case, it may not be "that" (you are not ALTERING the syntax
of the language, just USING it for the same purpose), but it's sure close.
(In Dylan, I do believe you could ``do that'' -- except the surface
syntax would not be Lisp-ish, of course).


> 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.
> 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.

If you consider than giving e.g. the variable name as an argument to
do-file-lines is the crucial issue here, then it's probably quite true
that this fundamental (?) feature "cannot be implemented any other way";
in Ruby, e.g., the variable name would not be an argument to dofilelines,
it would be a parameter at the start of the block receiving & using it:

   dofilelines(somefilename) do |l|
      collect l if someproperty? l
   end

However, it appears to me that the focus on where variable names are
to be determined may be somewhat misplaced.  The key distinction does
seem to be: if you're happy using a language as it was designed (e.g.,
in this example, respecting the language designer's concept that the
names for the control variables of a block must appear within | vertical
bars | at the start of the block -- or, in Python, the reversed concept 
that they must appear between the 'for' and 'in' in the "for ... in ...:
statement), macros are not relevant; if you do want to design and use
your own language (including, for example, placing variable names in 
new and interesting places) then macros can let you do that, while
other constructs would be insufficiently powerful.

If you dream of there being "preferably only one obvious way to do it",
as Pythonistas do (try "import this" at an interactive Python prompt),
macros are therefore a minus; if you revel in the possibilities of there
being many ways to do it, even ones the language designer had never even
considered (or considered and rejected in disgust:-), macros then become
a huge plus.

Therefore, I entirely agree that people who pine for macros should
use them in a language that accomodates them quite well, is designed
for them, cherishes and nurtures and exhalts them, like some language
of the Lisp family (be it Common, ISO, Scheme, ...), or perhaps Dylan
(which often _feels_ as if "of the Lisp family" even though it does
not use S-expressions), rather than trying to shoehorn them willy-nilly
into a language to whose overall philosophy they are SO utterly foreign,
like Python (Ruby, and even more Perl, may be a different matter;
google for "Lingua Latina Perligata" to see what Perl is already able
to do in terms of within-the-language language design and syntax
alteration, even without anything officially deemed to be 'macros'...
it IS, after all, a language CENTERED on "more than one way to do it").


Alex





More information about the Python-list mailing list