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

Kyle Lahnakoski klahnakoski at mozilla.com
Mon Nov 14 21:57:39 EST 2016


It would be nice if

....coalesce(EXPR1, EXPR2, EXPR3)

Evaluated the N+1 argument only if the Nth evaluated to None.  Of course 
this may break the general patterns in the Python language. Maybe we can 
fake it by wrapping the expressions in lambdas:

....coalesce(lambda: EXPR1(), lambda EXPR2(), lambda EXPR3())

and defining a `coalesce` as

def coalesce(*args):
....for a in args:
........a_val=a()
........if a_val is not None:
............return a_val
....return None

Making a coalesce call looks painful, but allowing the called function 
to control the evaluation of its parameters may be useful. Suppose we 
can pass methods to functions; using generators to do so: Let & refer to 
lazy-evaluated parameters:

....def coalesce(&a, &b):
........if a is None:
............return b
........else:
............return a
....
....c = coalesce(expr1(), expr2())

Would be converted to :

....def coalesce(a, b):
........a = yield
........a_val = a()
........if a_val is None:
............b = yield
............b_val = b()
............return b_val
........else:
............return a_val
....
....exprs = [expr1, expr2]
....temp = coalesce()
....for e in exprs:
........try:
............c = temp.next(e)
........except StopIteration:
............break

Or, even better...

....def coalesce(*&args):
........for a_val in args:
............if a_val is not None:
................return a_val
........return None
....
....c = coalesce(expr1(), expr2())

Gets converted to

....def coalesce(*args):
........for a in args:
............a_val = a()
............if a_val is not None:
................return a_val
........return None
....
....exprs = [expr1, expr2]
....temp = coalesce()
....for e in exprs:
........try:
............c = temp.next(e)
........except StopIteration:
............break

...or something like that.  I can not think of other reasons for this 
type of expansion; maybe logical `and` can be given a magic method: 
"__logand__":

....def __logand__(self, &other):
........if self:
............return True
........return o
....
....c = my_object and some_other()

which has a combination of immediately-evaluated parameters, and 
lazy-evaluated parameters:

class MyClass(object):
....def __logand__(self):
........if self:
............yield True
............return
........other = yield
........return other()
....
....exprs = [some_other]
....temp = MyClass.__logand__(my_object)
....for e in exprs:
........try:
............c = temp.next(e)
........except StopIteration:
............break

I hope that the acrobatics shown here might be easier to implement at a 
lower level; where in-line generator code collapses to simple branched 
logic.



On 11/13/2016 1:51 AM, Nick Coghlan wrote:
>
> At that point, if we did decide to offer a builtin instead of 
> dedicated syntax, the option I'd argue for is actually SQL's 
> "coalesce": coalesce(EXPR1) else coalesce(EXPR2) else EXPR3 Yes, it's 
> computer science jargon, but the operation itself is an odd one that 
> doesn't really have an established mathematical precedent or 
> grammatical English equivalent. Cheers, Nick.



More information about the Python-ideas mailing list