[Python-ideas] Alternative to PEP 532: delayed evaluation of expressions
Eric V. Smith
eric at trueblade.com
Sun Nov 6 08:06:27 EST 2016
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.
More information about the Python-ideas
mailing list