[Python-ideas] PEP 532: A circuit breaking operator and protocol

Nick Coghlan ncoghlan at gmail.com
Tue Nov 15 03:18:37 EST 2016


On 15 November 2016 at 17:13, Ryan Fox <ryan at rcfox.ca> wrote:
> I'm worried that the distinction between `or` and `else` will not be
> obvious. It seems like `else` will effectively just be `or`, but with more
> functionality.

The next draft makes that explicit: "and", "or" and PEP 505's "??"
would all just be syntactic sugar for "else" combined with particular
circuit breakers.

> I'm also still not convinced about the reasons to avoid implementing this on
> `or`. I'll address the points from the rationale:
>
>>  defining a shared protocol for both and and or was confusing, as __then__
>> was the short-circuiting outcome for or , while__else__ was the
>> short-circuiting outcome for and
>
> I wonder: Could the protocol be defined in terms of `or`, with DeMorgan's
> law applied behind the scenes in the case of `and`?
>
> ie:
>     existing(x) and existing(y)   =>   missing(y) or missing(x)

The next draft reverts to the symmetric API proposal from
pre-publication drafts, so this part of the rationale is gone.

>> the and and or operators have a long established and stable meaning, so
>> readers would inevitably be surprised if their meaning now became dependent
>> on the type of the left operand. Even new users would be confused by this
>> change due to 25+ years of teaching material that assumes the current
>> well-known semantics for these operators
>
> With basic pass-through implementations for __then__ and __else__ attached
> to all classes by default, and the existing __bool__, it seems like `or`
> would continue to function in the same way it currently does.

Except it would be a *lot* slower (as in, an-order-of-magnitude
slower, not a-few-percent slower).

The forced call to __bool__() in the second example below hints at the
likely cost of bypassing the existing optimised fast paths for
conditions that produce a boolean result:

$ python -m perf timeit -s "lhs = True; rhs = False" "lhs and rhs"
.....................
Median +- std dev: 16.6 ns +- 3.3 ns

$ python -m perf timeit -s "lhs = True; rhs = False" "lhs.__bool__() and rhs"
.....................
Median +- std dev: 113 ns +- 18 ns

Accordingly, we want interpreter implementations to be able to readily
distinguish between "normal" conditions (which would continue to just
be evaluated as boolean values in order to determine which branch to
take) and circuit breakers (which want to be able to further influence
the result *after* the interpreter has determined which branch to
evaluate)

> There are plenty of current dunder methods that are already redefined in
> ways that might confuse people: % on strings, set operators, etc.

None of those cases introduced a protocol method into an operation
that didn't previously use one - they instead borrowed existing
protocol driven operators for their own purposes.

>> Python interpreter implementations, including CPython, have taken
>> advantage of the existing semantics of and and or when defining runtime and
>> compile time optimisations, which would all need to be reviewed and
>> potentially discarded if the semantics of those operations changed
>
> I can't really speak to any of this, not being familiar with the internals
> of any implementation. Though, it might work out that some of the code for
> handling `and` and `or` could be thrown out, since those operators would be
> transformed into conditional expressions.

That's exactly the kind of outcome we *don't* want.

> I very much understand the desire to not break working, optimized
> implementations. However, this feels a little flimsy as a reason for
> introducing new syntax.

The language-design-driven reason is that "and" and "or" are terms
drawn from boolean logic, and hence can reasonably be expected to
implement that. We absolutely *could* say that they don't
*necessarily* implement boolean logic anymore, just as mathematical
operators don't necessarily represent the traditional arithmetic
operations, but I'd personally prefer the status quo to that possible
outcome.

The first draft of PEP 532 *did* propose doing things that way,
though: https://github.com/python/peps/commit/3378b942747604be737eb627df085979ff61b621

I never posted that version here, as I didn't really like it myself,
and had in fact already rewritten it to the current proposal by the
time I merged it into the main PEPs repo:
https://github.com/python/peps/commit/8f095cf8c0ccd4bf770e933a21e04b37afc53cfe
:)

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list