[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==========--