On Wed, Oct 11, 2017 at 7:46 AM, Steve Dower email@example.com wrote:
Nick: “I like Yury's example for this, which is that the following two examples are currently semantically equivalent, and we want to preserve that equivalence:
with decimal.localcontext() as ctx: ctc.prex = 30 for i in gen(): pass g = gen() with decimal.localcontext() as ctx: ctc.prex = 30 for i in g: pass”
I’m following this discussion from a distance, but cared enough about this point to chime in without even reading what comes later in the thread. (Hopefully it’s not twenty people making the same point…)
I HATE this example! Looking solely at the code we can see, you are refactoring a function call from inside an explicit context manager to outside of it, and assuming the behavior will not change. There’s absolutely no logical or semantic reason that these should be equivalent, especially given the obvious alternative of leaving the call within the explicit context. Even moving the function call before the setattr can’t be assumed to not change its behavior – how is moving it outside a with block ever supposed to be safe?
Exactly. You did say it less politely than I did, but this is exactly how I thought about it. And I'm not sure people got it the first time.
I appreciate the desire to be able to take currently working code using one construct and have it continue working with a different construct, but the burden should be on that library and not the runtime. By that I mean that the parts of decimal that set and read the context should do the extra work to maintain compatibility (e.g. through a globally mutable structure using context variables as a slightly more fine-grained key than thread ID) rather than forcing an otherwise straightforward core runtime feature to jump through hoops to accommodate it.
In fact, one might then use the kind of enhanced context-local storage
that I've been planning on top of PEP 555, as also mentioned in the PEP.
It would not be the recommended way, but people might benefit from it in
some cases, such as for a more backwards-compatible PEP-555 "feature
enablement" or other special purposes like
(However, these needs can be satisfied with an even simpler approach in PEP 555, if that's where we want to go.)
I want PEP 555 to be how things should be, not how things are. After all, context arguments are a new feature. But plenty of effort in the design still goes into giving people ways to tweak things to their special needs and for compatibility issues.
New users of this functionality very likely won’t assume that TLS is the semantic equivalent, especially when all the examples and naming make it sound like context managers are more related. (I predict people will expect this to behave more like unstated/implicit function arguments and be captured at the same time as other arguments are, but can’t really back that up except with gut-feel. It's certainly a feature that I want for myself more than I want another spelling for TLS…)
I assume you like my decision to rename the concept to "context arguments" :). And indeed, new use cases would be more interesting than existing ones. Surely we don't want new use cases to copy the semantics from the old ones which currently have issues (because they were originally designed to work with traditional function and method calls, and using then-available techniques).