[Python-Dev] Extended Function syntax

Andrew McGregor andrew@indranet.co.nz
Sun, 02 Feb 2003 10:56:29 +1300


--==========1605285482==========
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline



--On Saturday, February 01, 2003 15:02:51 -0500 Guido van Rossum=20
<guido@python.org> wrote:


> Many users have only a vague notion of the difference between a
> syntactic construct and a function (witness repeated suggestions here
> and elsewhere that confuse these matters), yet nobody has a problem
> with understanding the difference in scope rules between 'def' and
> 'if' statements, for example.  So I hope that learning that some
> functions treat a thunk's scope different than others is no huge
> burden.

I think it's not a burden, and in fact the ability to make such things=20
would be very useful.  I've come up with a few examples below.

> In some Lisps, I believe a function can determine whether its
> arguments are evaluated before the call (=3D=3D normal Python semantics)
> or whether they are somehow (I don't know the details!) passed to the
> function as parse trees so the function can evaluate them at will.
> This does not seem to confuse Lisp users.  Or does it?  I honestly
> don't know if this is popular or considered a wart.

It's fundamental.  Lisp syntax is almost exactly a representation of the=20
parser output; lisps basically don't have a parser, up to syntactic sugar.=20
So a macro definition can specify in its definition which arguments are=20
passed evaluated or unevaluated (in some lisps, this is done by explicity=20
evaluating the appropriate arguments; elisp is one such).  The difference=20
between macros and functions comes down to if arguments are evaluated or=20
not.

Lots of ordinary, standard lisp idiom is implemented this way, even such=20
things as 'let', which is the local variable binder and even sometimes=20
'defun', which is the function definer.

For example, in xemacs elisp syntax for simplicity, meaning all macro=20
arguments are passed unevaluated, defun is approximately defined as (up to=20
the docstring handling):

(defmacro defun (name arglist &optional docstring &rest body)
  (quote (setf name (function (lambda arglist . body))))
  ; return code to set the docstring here
  )

which has an effect essentially identical to python's 'def' keyword.  The=20
return value of the macro (and what is actually compiled) is

(setf name (function (lambda arglist . body)))

(substituting the passed in arguments) which when executed binds name to=20
the compiled version of the lambda.

So if we then do:

(defun inc (a)
  (add 1 a))

it compiles as if it were:

(setf 'inc (function (lambda (a) . ((add 1 a))))))

For a really huge demonstration of what lisp macros can do, see advice.el=20
from an emacs distribution.  It's a good example because it's extremely=20
well commented, and some of the comments show the downsides of macros as=20
well.

> I do worry that if my thunks are to be maximally useful, they may end
> up having all variables bound through "cells" (as used for nested
> scopes) which may give them a reputation of being slow compared to
> in-line code, which would limit their popularity for control flow
> statements like synchronized().
>
> --Guido van Rossum (home page: http://www.python.org/~guido/)

Well, they could be treated like lisp macros, that is as source code (or,=20
potentially, parse tree) transformation functions, and compiled just like=20
any other code.  This would probably be the simplest to implement, and=20
would allow such things to work with no slowdown at all.

For example, presuming python had a defmacro that took all arguments=20
unevaluated and can return a block, that @block introduces a block=20
argument, and that interpolate(block) compiles the block instead of itself, =

we could have:

defmacro synchronized(lck, @block):
    return:
        lck.acquire()
        try:
            interpolate(block)
        finally:
            lck.release()

synchronized(lock):
    # text of block here
    pass

which compiles identically to:

lck.acquire()
try:
    # text of block here
    pass
finally:
    lck.release()

Then:

defmacro property(name, docstring, @get, @set, @delete):
    def getter interpolate(get.arglist):
        interpolate(get.block)
    def setter interpolate(set.arglist):
        interpolate(set.block)
    def deleter interpolate(delete.arglist):
        interpolate(delete.block)
    return:
        name =3D property(docstring, getter, setter, deleter)

class Circle(object):
   property circumference, """Circumference of the circle""":
       get (self):
           return self.radius*6.283
       set (self, value):
           self.radius =3D value/6.283
       delete (self):
           del self.radius

would do the obvious thing using block keyword arguments (I can't think how =

to express the anonymous scope implied here in regular Python; the=20
intention is that getter and friends are defined in the local namespace of=20
the macro invocation).

Of course the proposed syntax is hypothetical, but I think it's quite =
clean.

Andrew


    
--==========1605285482==========
Content-Type: application/pgp-signature
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE+PEKNHamGxvX4LwIRAon5AJ9bWwgsr+kmbyThK9Fd2hlrMBzWlACgxMW/
WsHNN+4wPAoRgM52G/ctD70=
=KiOp
-----END PGP SIGNATURE-----

--==========1605285482==========--