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

Nathaniel Smith njs at pobox.com
Sun Nov 6 20:32:29 EST 2016


On Sun, Nov 6, 2016 at 5: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.

If we're considering options along these lines, then I think the local
optimum is actually a "quoted-call" operator, rather than a quote
operator. So something like (borrowing Rust's "!"):

    eval_else!(foo.bar, some_func())

being sugar for

    eval_else.__macrocall__(<unevaluated thunk of foo.bar>,
<unevaluated thunk of some_func()>)

You can trivially use this to recover a classic quote operator if you
really want one:

    def quote!(arg):
        return arg

but IMO this way is more ergonomic for most use cases (similar to your
'&' suggestion), while retaining the call-site marking that "something
magical is happening here" (which is also important, both for
readability + implementation simplicity -- it lets the compiler know
statically when it needs to retain the AST, solving the issue that
Greg pointed out).

Some other use cases:

Log some complicated object, but only pay the cost of stringifying the
object if debugging is enabled:

    log.debug!(f"Message: {message_object!r}")

Generate a plot where the axes are automatically labeled "x" and
"np.sin(x)" (this is one place where R's plotting APIs are more
convenient than Python's):

    import numpy as np
    import matplotlib.pyplot as plt
    x = np.linspace(0, 10)
    plt.plot!(x, np.sin(x))

What PonyORM does, but without the thing where currently their
implementation involves decompiling bytecode...:

    db.select!(c for c in Customer if sum(c.orders.price) > 1000)

Filtering out a subset of rows from a data frame in pandas; 'height'
and 'age' refer to columns in the data frame (equivalent to
data_frame[data_frame["height"] > 100 and data_frame["age"] < 5], but
more ergonomic and faster (!)):

    data_frame.subset!(height > 100 and age < 5)

(IIRC pandas has at least experimented with various weird lambda hacks
for this kind of thing; not sure what the current status is.)

Every six months or so I run into someone who's really excited about
the idea of adding macros to python, and I suggest this approach. So
far none of them have been excited enough to actually write a PEP, but
if I were going to write a PEP then this is the direction that I'd
take :-).

-n

-- 
Nathaniel J. Smith -- https://vorpus.org


More information about the Python-ideas mailing list