Subject: Python vs. Lisp/Scheme

From: Omer Zak <[email-masked]>
Date: Wed, 13 Aug 2003 23:33:04 +0300

Peter Norvig wrote an article titled "Python for Lisp Programmers"
(at URL: http://www.norvig.com/python-lisp.html).

The surprising conclusion is that the only major Lisp feature missing
from Python is the Lisp macros.

I assume that the article is not completely up-to-date, so I am asking
any Python experts on Hackers-IL if they know of any PEP or
implementation which addresses the issue of macros in Python (and I
mean Lisp-like macros, not the lame C preprocessor macros).

                                             --- Omer
My opinions, as expressed in this E-mail message, are mine alone.
They do not represent the official policy of any organization with which
I may be affiliated in any way.
WARNING TO SPAMMERS:  at http://www.zak.co.il/spamwarning.html

---

From: Beni Cherniavsky <[email-masked]>
Date: Thu, 14 Aug 2003 20:20:20 +0300 (IDT)

> from Python is the Lisp macros.
Yep, Python is known as "Lisp without the paraentheses" and part of
the implication is that it's without macros.

> mean Lisp-like macros, not the lame C preprocessor macros).
There were repeated heated discussions on this.  Search c.l.python if
you have some spare days for reading ;-).  They went in two
directions:

- Do we want to have macros in Python at all or will they be abused?

  This was mainly a religious discussion; those that had tasted lisp
  macros (me included) argue they are a Good Thing while those who
  only have sober experience with C macros or who didn't like lisp
  think Joe Average shouldn't be trusted with such a powerful thing.
  This curiously deviates from Python's approach of "we are all adults
  here" but it's true that real macros change a language's shape
  considerably.

- What would Pythonic macros look like?  Should they be hygienic or
  not?  What syntax should they have?  Desinging a pythonic macro
  system is surprisingly hard (believe me, I've been thinking about it
  since I learnt Python ;-).

  One of the reasons is that Python is desinged to provide an illusion
  of no previous compilation step (it exists but only as an
  optimization).  Intorducing one would be bad.  Very.  It should be
  possible to e.g. import macros from libraries - but imports happen
  at run time.  This constrains your freedom a lot.

The general concensus is that they main thing people want is to define
their own block statements.  Expression-level macros are low priority.
There are no PEPs providing macros.  The only related one is `PEP
310`__ which addresses the main use case cited for macros -
abstracting try..finally pairs (lisp has the with-foo family of macros
which address it very elegantly).

__ http://www.python.org/peps/pep-0310.html

-- 
Beni Cherniavsky <[email-masked]>

---

From: Omer Zak <[email-masked]>
Date: Fri, 15 Aug 2003 10:00:40 +0300

>Omer Zak wrote on 2003-08-13:
[... snipped ...]

>you have some spare days for reading ;-).  They went in two
[... interesting details were snipped ...]

I just love this kind of answer to my exploratory questions!

>__ http://www.python.org/peps/pep-0310.html
This PEP mentions another proposed control structure, which is 
implemented by means of a try-finally block.
This leads me to the question whether there is any control structure, 
which cannot be implemented in Python by means of a try-except and/or 
try-finally blocks.
                                             --- Omer
My opinions, as expressed in this E-mail message, are mine alone.
They do not represent the official policy of any organization with which
I may be affiliated in any way.
WARNING TO SPAMMERS:  at http://www.zak.co.il/spamwarning.html

---

From: Beni Cherniavsky <[email-masked]>
Date: Fri, 15 Aug 2003 16:37:21 +0300 (IDT)

> which cannot be implemented in Python by means of a try-except and/or
I'm not sdure I understand your question correctly.  Do you mean that
since the proposed ``with`` construct is syntax sugar for ``try``, you
suspect that all constructs are syntax sugars for it?  The basic
answer is negative - how would ``try`` implement loops, conditionals,
``def`` and ``class``?  In non-abused usage, exceptions are only goof
exiting from constructs.  Stopping a for loop is already implemented
with the `StopIteration` exception and ``break``/``continue`` could be
implemented so (but aren't).

But if we abuse them, given only ``def``, function call and
exceptions, we could "implement" other constructs.  Here is a
conditional::

    def if(condition, body_if_true, body_if_false)
    try:
        1 / bool(condition())
    except ZeroDivisionError:  # Evil me, hahaha ;-)
        body_if_false()
    else:
        body_if_true()

(this has a bug of masking `ZeroDivisionError` when evaluating the
condition and assumes `bool` is builtin; what we really need is any
construct to raise different exceptions (or none) depending on the
thruthness of the expression and it's suspicuous to assume we have one
if we don't have conditionals...)

From here it's standard Scheme tricks to implement looping from
recursion (if we forget about the fact that Python doesn't do tail
recursion (because it uses the C stack)).  Here is while::

    def while_(cond, body):
        try:
            if cond:
                try:
                    body()
                except Continue_:
                    pass
                while_(cond, body)
        except Break_:
            pass

(using if as syntax sugar for the above "implementation" and assuming
``break`` raises `Break_` and ``continue`` raises `Continue_`, which
is not how they work - they are only valid lexically inside a loop and
compile to simple goto opcodes).  Using this while, here is for::

    def for_(iterable, body):
        i = iter(iterable)
        while True:
            try:
                item = i.next()
            except StopIteration:
                break
            body(item)

All this sidesteps issues of scoping; scoping in Python is implemented
at compile time (which is one of the problems with macros) and the
above definitions would not be useful because the bodies you supply to
them can't modify variables in outer scopes (which is a problem with
Python's scoping, a compromise to avoid declarations).

And obviously, Python is not implemented in such hideous ways ;-).