[Python-Dev] Extended Function syntax (original) (raw)

Guido van Rossum guido@python.org
Thu, 30 Jan 2003 21:02:23 -0500


> How about > > foo = property: > ...

That looks quite nice to me, if it can be parsed...

That's the first bit of positive feedback I got on this proposal all day. :-)

Fortunately the second bit came soon (although Raymond left in an attribution to Greg Ewing that seemed to imply it was his idea :-).

And as another opening remark, I didn't mean this to replace the [prop, prop, ...] syntax on function/method defs -- that seems a useful addition in its own right. I'm not so sure that it also applies to classes -- there we already have metaclasses.

The form above can be parsed easily, if we say that "colon followed by suite" is allowed only at the end of a simple assignment or expression statement. Then,

v = e: S

would be equivalent to

v = e(T)

where T is a thunk created from S. (More about thunks below.) Similarly,

e: S

woul be equivalent to

e(T)

Note that 'property' and 'synchronized' used as examples are not new keywords! They are just built-in objects that have the desired semantics and know about thunks. Syntactically, this would be valid too:

x = 42: print "Hello world"

but when executing it, you'd get a TypeError when calling the number 42 with a thunk as argument.

Other small stuff:

v, w = e: S

is of course equivalent to

v, w = e(T)

and similarly

v = w = e: S

is equivalent to

v = w = e(T)

Another exercise to see if this construct can be used flexibly might be to see if we can use it for creating an interface syntax inspired by Zope interfaces. Let's see...

I1 = interface(I2, I3): def m1(a, b, c): "docs for method m1" def m2(a, b, c): "docs for method m2"

This doesn't look very attractive, certainly not much better than Zope's abuse of the class keyword.

BTW, I'd be interested in entertaining different syntactic proposals that would make it less likely that a typo would accidentally turn something into a thunk call. It worries me that leaving out 'def' in a simple function definition could turn it into code that gives a TypeError when executed rather than a SyntaxError. (Forgetting 'def' is a mistake I frequently make.)

Samuele rightly questioned the semantics of the thunk with regard to namespaces though. In the example of a locking section:

synchronized(aLock): BLOCK

you'd want the scope of BLOCK to be the same as that of the surrounding code, just as with an if or try statement.

Note that synchronized() must be a function of one argument that returns a function. Something like this:

def synchronized(lock): def f(thunk): lock.acquire() try: thunk() finally: lock.release() return f

On the other hand, when using the same syntax to define a property:

foo = property: "docs" def get(self): return self.__foo def set(self, value): self.__foo = value

we would like the thunk to be executed in a new scope.

In this case, the property constructor must (1) detect that it is being called with a thunk argument, and (2) in that case, call the thunk (so the def statements inside the thunk are executed) and somehow extract them from the thunk's namespace.

I don't mind if a function that takes a thunk has to work a little harder (although there should be a simple case that's roughly equivalent to Ruby blocks). I also don't mind making a thunk an object with various pieces of metadata and perhaps different methods so that synchronized and property can pick it apart and call it in different ways.

If we didn't have local variable optimizations or nested scopes, and all namespaces were simply dictionaries, then it might be sufficient if a thunk were a code object. synchronized() could use this to execute its thunk in its caller's scope:

exec thunk in sys._getframe(1).f_locals

and property could execute it in a freshly created namespace:

d = {}
exec thunk in d

but alas, life isn't that simple any more. A thunk may:

Worse, you may have multiple thunks, and they may set variables with the same name (e.g. all properties will tend to define get and set).

Also, if a global is used in the scope containing a thunk and set in the thunk, does that make it a local variable shared between the containing scope and the thunk? Or does it make it a local variable in the thunk that shadows the global only in the thunk's scope?

The compiler could generate code for thunks that leave the decision on all these issues up to the code (e.g. the implementation of property or synchronized) that calls the thunk. Maybe the cells used to implement nested scopes can help. But it would be pretty hairy!

--Guido van Rossum (home page: http://www.python.org/~guido/)