Re: Jim Fulton : Extensible compound statements -- new exec flavor

Jim Fulton (jfulton@disqvarsa.er.usgs.GOV)
Sun, 5 Feb 1995 01:05:05 GMT

>>>>> "Guido" == Guido van Rossum <Guido.van.Rossum@cwi.nl> writes:
In article <9502041329.AA23329=guido@voorn.cwi.nl> Guido.van.Rossum@cwi.nl writes:

> I'd like to see if I can bring this discussion to a conclusion.
> Rather than quoting the various elaborate messages, I'll try to
> summarize the issues:

> (Control): It's important to know at cleanup time whether and how the
> user's suite was completed, and if it raised an exception, which one.

> (Cleanliness): It's important to hide from the end user the machinery
> that's used to implement the transaction or locking protocol.

> (Nestability): It must be possible to safely implement nested
> transactions.

> There are two concrete proposals for new syntax and semantics (apart
> from variants about which keyword to use and whether to combine it
> with else or except clauses):

> (Ty Sarna):

> enter object:
> suite

> calls object.__enter__(), then executes suite, then calls
> object.__leave__() with an indication of whether suite terminated with
> an exception or not

> (Jim Fulton):

> enter object:
> suite

> compiles suite into a code object, and calls object(suite_code, globals,
> locals) where globals and locals are the dictionaries containg global
> and local variables of the scope containing the enter statement.

> (Jim Roskind and I have proposed several ways of doing this without
> changes to the language, using destructors and/or try...finally, but
> neither of these satisfies the "cleanliness" requirement.)

Using destructors does not give adequate control over timing.

> Now for my response.

> My concern with Jim's proposal is that it uses code objects. These
> are a mechanism that is part of the language's current implementation.
> It is a Bad Thing to use these to describe semantics for the language.
> Code objects are currently used in two places: to implement ".pyc"
> files, and returned by the compile() function to be passed into exec
> or eval(). The former use is clearly purely a matter of
> implementation. The latter is fairly esoteric, and is in fact an
> ad-hoc measure to enable certain optimizations. As the language
> designer, I want to keep code objects out of the specification of the
> semantics of my language (though they could be a common extensions).

OK. The reason I suggested using code objects is that they are part
of the current "exec" semantics, which means that they are part of the
current language semantics. If you want to get rid of them, it's your
language. :-)

I would like to have something similar, but I can live without it.
It would be useful for some applications (other than TP) to be able to
control when (and even if) the suite is executed. When I first
suggested this, my thought that, to justify changing the language,
the mechansism should be very general. My thought was to give a way
to implement almost any sort of application-defined control
structures.

Note that what I was proposing is similar in many ways to Smalltalk
blocks, which are first-class objects that contain code and context
and that can be used to implement arbitraty control structures. In
fact, all of Smalltalk's control statements are defined in terms of
blocks. I have used them to define an exception handling mechanism
and to specify Motif callbacks for GNU Smalltalk.

> This is why I much prefer Ty's proposal: it doesn't need code objects.
> Jim sees problems with it -- I think he is afraid that it doesn't
> satisfy the Control and Nestability requirements.

No. I think Ty's proposal will handle nested transaction (at least
the ones I'm thinking about) just fine. But it will *not* handle
applications like:

exec thread:
...a suite that runs in a separate thread...

or

exec widget.addCallback('callback_name'):
...a suite that is executed when a widget gets a callback...

> For Control, the
> crucial thing is to know how and why the suite was terminated.
> Fortunately, this is easily accomplished: just pass the exception
> type, value and traceback as parameters to the __leave__ method
> (making them None if the suite terminated without raising an exception).

> We then get the following semantics:

> enter object:
> suite

> 1. Call object.__enter__(). If this raises an exception, execution of
> the enter statement is abandoned.

> 2. Execute the suite.

> 3. If suite terminates without raising an exception (not counting
> exceptions handled within it), call object.__leave__(None, None, None).

> 4. If the suite terminates with an exception, call
> object.__leave__(exc_type, exc_value, exc_traceback).

> 5. In either case, if the call to object.__leave__(...) terminates
> normally, the enter statement terminates normally; if it raises an
> exception, it may be handled in the scope containing the enter
> statement. (There should be a way to emulate the effect of "finally",
> which is not to disturb the stack trace. This is a separate, and
> minor issue.)

Cool.

> It is possible that there are race conditions in this proposal that
> worry the transaction processing experts (which I'm not).

I don't see one. Do you see any race condition with simple locking?

> A refinement of the proposal could be to save the return value of
> object.__enter__(), and call *its* __leave__ method. Then the
> destructor for this object could check whether its __leave__ had been
> called at all or not, and if not, conclude that this was caused by an
> exception that occurred in the __enter__ method. Come to think of it
> we can almost rewrite the semantics in terms of try...finally:

> _tmp = object.__enter__()
> try:
> suite
> finally:
> _tmp.__leave__(sys.exc_type, ...)

> except that the current system doesn't give sys.exc_type etc. the
> right values when the finally clause is entered.

> A simplicfication could be to just call object() and _tmp(...) rahter
> than __enter__ and __leave__ methods.

This requires the use of a temporary variable and seems to be complex
enough to encourage programming errors. I like the proposed syntax
*much* better.

> Note that this should also make it easier to keep track of nested
> transactions on the same object.

I'm not sure what this means. If you mean keeping track of which
transaction have accessed an object, I don't see the connection.

> Some less important points:

> - The choice of a keyword. How about 'in'? ("in object: suite")

Fine with me, especially since it reuses a keyword.

> - Combining this with except clauses. I'm not for it, if only because
> there's a potential confusion: does

> enter object:
> suite
> except:
> handler

> mean the same as

> enter object:
> try:
> suite
> except:
> handler

> or the same as

> try:
> enter object:
> suite
> except:
> handler

> I could probably defend either way, which means that it will always
> confuse a fraction of the users. (For the same reason it's not
> allowed to combine except and finally clauses on a try statement.)

I don't mind not combining with except.

--
-- Jim Fulton      jfulton@mailqvarsa.er.usgs.gov    (703) 648-5622
                   U.S. Geological Survey, Reston VA  22092 
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.