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.

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to