[Python-ideas] Alternative to PEP 532: delayed evaluation of expressions

Eric V. Smith eric at trueblade.com
Sun Nov 6 09:31:06 EST 2016


[top posting from my phone]

Chris Angelico points out the & part of the idea interacts poorly with *args and **kwargs, so I drop that idea. 

Re-reading PEP 312, this idea is basically identical, with different spellings. 

The point remains: do we want  to be able to create unevaluated expressions that can be evaluated at a different point?

--
Eric.

> On Nov 6, 2016, at 8:06 AM, Eric V. Smith <eric at trueblade.com> wrote:
> 
> Creating a new thread, instead of hijacking the PEP 532 discussion.
> 
> From PEP 532:
> 
> > Abstract
> > ========
> >
> > Inspired by PEP 335, PEP 505, PEP 531, and the related discussions, this PEP
> > proposes the addition of a new protocol-driven circuit breaking operator to
> > Python that allows the left operand to decide whether or not the expression
> > should short circuit and return a result immediately, or else continue
> > on with evaluation of the right operand::
> >
> >     exists(foo) else bar
> >     missing(foo) else foo.bar()
> 
> Instead of new syntax that only works in this one specific case, I'd prefer a more general solution. I accept being "more general" probably seals the deal in killing any proposal!
> 
> I realize the following proposal has at least been hinted at before, but I couldn't find a specific discussion about it. Since it applies to the short-circuiting issues addressed by PEP 532 and its predecessors, I thought I'd bring it up here. It could also be used to solve some of the problems addressed by the rejected PEP 463 (Exception-catching expressions). See also PEP 312 (Simple Implicit Lambda). It might also be usable for some of the use cases presented in PEP 501 (General purpose string interpolation, aka i-strings).
> 
> I'd rather see the ability to have unevaluated expressions, that can later be evaluated. I'll use backticks here to mean: "parse, but do not execute the enclosed code". This produces an object that can later be evaluated with a new builtin I'll call "evaluate_now". Obviously these are strawmen, and partly chosen to be ugly and unacceptable names and symbols in the form I'll discuss here.
> 
> Then you could write a function:
> 
> eval_else(`foo.bar`, `some_func()`)
> 
> whose value is foo.bar, unless foo.bar cannot be evaluated, in which case the value is some_func().
> 
> def eval_else(expr, fallback, exlist=(AttributeError,)):
>    try:
>        return evaluate_now(expr)
>    except exlist:
>        return evaluate_now(fallback)
> 
> Exactly which exceptions you catch is up to you. Of course there's the chance that someone would pass in something for which the caught exception is too broad, and it's raised deep inside evaluating the first expression, but that's no different than catching exceptions now. Except I grant that hiding the try/except inside a called function increases the risk.
> 
> Like f-strings, the expressions are entirely created at the site they're specified inside ``. So they'd have access to locals and globals, etc., at the definition site.
> 
> def x(foo, i):
>    return eval_else(`foo.bar`, `some_func(i, __name__)`)
> 
> And like the expressions in f-strings, they have to be valid expressions. But unlike f-strings, they aren't evaluated right when they're encountered. The fact that they may never be evaluated is one of their features.
> 
> For example the if/else expression:
> 
> if_else(`y`, x is None, `x.a`)
> 
> could be defined as being exactly like:
> 
> y if x is None else x.a
> 
> including only evaluating x.a if x is not None.
> 
> def if_else(a, test, b):
>    if test:
>        return evaluate_now(a)
>    return evaluate_now(b)
> 
> You could do fancier things that require more than 2 expressions.
> 
> Whether `` returns an AST that could later be manipulated, or it's something else that's opaque is another discussion. Let's assume it's opaque for now.
> 
> You could go further and say that any argument to a function that's specially marked would get an unevaluated expression. Suppose that you can mark arguments as & to mean "takes an unevaluated expression". Then you could write:
> 
> def if_else(&a, test, &b):
>    if test:
>        return evaluate_now(a)
>    return evaluate_now(b)
> 
> And call it as:
> if_else(y, x is None, x.a)
> 
> But now you've made it non-obvious at the caller site exactly what's happening. There are other downsides, such as only being able to create an unevaluated expression when calling a function. Or maybe that's a good thing!
> 
> In any event, having unevaluated expressions would open up more possibilities than just the short-circuit evaluation model. And it doesn't involve a new protocol.
> 
> Eric.
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
> 



More information about the Python-ideas mailing list