<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 11 October 2017 at 21:58, Koos Zevenhoven <span dir="ltr"><<a href="mailto:k7hoven@gmail.com" target="_blank">k7hoven@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div style="font-family:monospace,monospace"><span style="font-family:arial,sans-serif">On Wed, Oct 11, 2017 at 7:46 AM, Steve Dower </span><span dir="ltr" style="font-family:arial,sans-serif"><<a href="mailto:steve.dower@python.org" target="_blank">steve.dower@python.org</a>></span><span style="font-family:arial,sans-serif"> wrote:</span><br></div><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div lang="EN-US"><div class="gmail-m_-7721987640868167855m_944848893627683382WordSection1"><p class="MsoNormal">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:</p><span class="gmail-"><span><p class="MsoNormal"> </p><p class="MsoNormal"> with decimal.localcontext() as ctx:</p><p class="MsoNormal"> ctc.prex = 30</p><p class="MsoNormal"> for i in gen():<br> pass<br><br> g = gen()</p><p class="MsoNormal"> with decimal.localcontext() as ctx:</p><p class="MsoNormal"> ctc.prex = 30</p></span></span><p class="MsoNormal"> for i in g:<br> pass”</p><p class="MsoNormal"> </p><p class="MsoNormal">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…)</p><p class="MsoNormal"> </p><p class="MsoNormal">I HATE this example! Looking solely at the code we can see, you are refactoring a function call from inside an *<b>explicit</b>* context manager to outside of it, and assuming the behavior will not change. There’s *<b>absolutely no</b>* 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?</p><p class="MsoNormal"> </p></div></div></blockquote><div><br></div><div><div style="font-family:monospace,monospace">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.</div></div></div></div></div></blockquote><div><br></div>Refactoring isn't why I like the example, as I agree there's no logical reason why the two forms should be semantically equivalent in a greenfield context management design.</div><div class="gmail_quote"><br></div><div class="gmail_quote">The reason I like the example is because, in current Python, with the way generators and decimal contexts currently work, it *doesn't matter* which of these two forms you use - they'll both behave the same way, since no actual code execution takes place in the generator iterator at the time the generator is created.</div><div class="gmail_quote"><br></div><div class="gmail_quote">That means we have a choice to make, and that choice will affect how risky it is for a library like decimal to switch from using thread local storage to context local storage: is switching from thread locals to context variables in a synchronous context manager going to be a compatibility break for end user code that uses the second form, where generator creation happens outside a with statement, but use happens inside it?</div><div class="gmail_quote"></div><div class="gmail_quote"><div class="gmail_quote"><br></div><div class="gmail_quote">Personally, I want folks
maintaining context managers to feel comfortable switching from thread local storage to context
variables (when the latter are available), and in particular, I want the decimal module to be able to make such a
switch and have it be an entirely backwards compatible change for synchronous
single-threaded code.<br></div></div><div class="gmail_quote"><br></div><div class="gmail_quote">That means it doesn't matter to me whether we see separating generator (or context manager) creation from subsequent use is good style or not, what matters is that decimal contexts work a certain way today and hence we're faced with a choice between:</div><div class="gmail_quote"><br></div><div class="gmail_quote">1. Preserve the current behaviour, since we don't have a compelling reason to change its semantics</div><div class="gmail_quote">2. Change the behaviour, in order to gain <end user benefit></div><div class="gmail_quote"><br></div><div class="gmail_quote">"I think it's more correct, but don't have any specific examples where the status quo subtly does the wrong thing" isn't an end user benefit, as:</div><div class="gmail_quote">- of necessity, any existing tested code won't be written that way (since it would be doing the wrong thing, and will hence have been changed)</div><div class="gmail_quote">- future code that does want creation time context capture can be handled via an explicit wrapper (as is proposed for coroutines, with event loops supplying the wrapper in that case)<br></div><div class="gmail_quote"><br></div><div class="gmail_quote">"It will be easier to implement & maintain" isn't an end user benefit either, but still a consideration that carries weight when true. In this case though, it's pretty much a wash - whichever form we make the default, we'll need to provide some way of switching to the other behaviour, since we need both behavioural variants ourselves to handle different use cases.</div><div class="gmail_quote"><br></div><div class="gmail_quote">That puts the burden squarely on the folks arguing for a semantic change: "We should break currently working code because ...".</div><div class="gmail_quote"><br></div><div class="gmail_quote">PEP 479 (the change to StopIteration semantics) is an example of doing that well, and so is the proposal in PEP 550 to keep context changes from implicitly leaking *out* of generators when yield or await is used in a with statement body.</div><div class="gmail_quote"><br></div><div class="gmail_quote">The challenge for folks arguing for generators capturing their creation context is to explain the pay-off that end users will gain from our implicitly changing the behaviour of code like the following:<br></div><div class="gmail_quote"><br></div><div class="gmail_quote"> >>> data = [sum(Decimal(10)**-r for r in range(max_r+1)) for max_r in range(5)]<br> >>> data<br> [Decimal('1'), Decimal('1.1'), Decimal('1.11'), Decimal('1.111'), Decimal('1.1111')]</div><div class="gmail_quote"> >>> def lazily_round_to_current_context(data):<br> ... for d in data: yield +d<br> ... <br></div><div class="gmail_quote"> >>> g = lazily_round_to_current_context(data)<br> >>> with decimal.localcontext() as ctx:<br> ... ctx.prec = 2<br> ... rounded_data = list(g)<br> ... </div><div class="gmail_quote"> >>> rounded_data<br></div><div class="gmail_quote"> [Decimal('1'), Decimal('1.1'), Decimal('1.1'), Decimal('1.1'), Decimal('1.1')]</div><div class="gmail_quote"><br></div><div class="gmail_quote">Yes, it's a contrived example, but it's also code that will work all the way back to when the decimal module was first introduced. Because of the way I've named the rounding generator, it's also clear to readers that the code is aware of the existing semantics, and is intentionally relying on them.<br></div><div class="gmail_quote"><br></div><div class="gmail_quote">The current version of PEP 550 means that the decimal module can switch to using context variables instead of thread local storage, and the above code won't even notice the difference.</div><div class="gmail_quote"><br></div><div class="gmail_quote">However, if generators were to start implicitly capturing their creation context, then the above code would break, since the rounding would start using a decimal context other than the one that's in effect in the current thread when the rounding takes place - the generator would implicitly reset it back to an earlier state.<br></div><div class="gmail_quote"></div><br clear="all"></div><div class="gmail_extra">Cheers,</div><div class="gmail_extra">Nick.</div><div class="gmail_extra"><br></div><div class="gmail_extra">-- <br><div class="gmail_signature">Nick Coghlan | <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a> | Brisbane, Australia</div>
</div></div>