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

Mikhail V mikhailwas at gmail.com
Sun Nov 6 22:58:58 EST 2016


On 7 November 2016 at 02:32, Nathaniel Smith <njs at pobox.com> wrote:
> 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 :-).
>

Oh great! Good to know I am not alone thinking in this
direction.
I have however one minor problem here: the problem is that "!" sign
is almost invisible in the code, unless there is syntax highlighting
which paints it in some very bright color.
On the other hand I am not sure if it *must* be very visible...
So to make it more distinctive in code I would propose something
like:

macros<>( x, y )
macros>( x, y )
macros::( x, y )

And those are already used operators, sad :(
It would look so neat... But probably still possible to do?


Mikhail


More information about the Python-ideas mailing list