[Python-ideas] Another way to avoid clumsy lambdas, while adding new functionality

Steven D'Aprano steve at pearwood.info
Wed Mar 5 12:18:23 CET 2014


On Tue, Mar 04, 2014 at 11:09:02PM +0000, Carl Smith wrote:
> The tentatively proposed idea here is using dollar signed expressions to
> define 'bills'. A bill object is essentially an expression which can be
> evaluated any number of times, potentially in different scopes.

Please don't invent "cute" names for things which already have names. 
What you are describing could be considered a thunk, or a macro, or a 
lazily-evaluated expression, depending on the precise details of how it 
works. But calling it a "bill" just because it starts with a $ just 
makes me cringe.


> The following expression [a bill literal] would be pointless, but would
> define a bill that always evaluates to 1.
> 
>     $1
> 
> So, eval($1)==1.

My personal feeling here is that if you have to explicitly call eval on 
the "bill" to evaluate it, it's not worth doing. If we were happy with 
that, we've already got compile(), or we have functions. Or just eval a 
string directly. My feeling is that evaluating the expression needs to 
be implicit, performed at need rather than up front, rather in the same 
way that short-circuiting operators don't actually evaluate the 
expression unless needed:

    x or expr  # expr is only evaluated if x is a falsey value


Yes, I know, the Zen of Python says that "explicit is better than 
implicit", but the Zen is intended as guidelines, not thought-blockers. 
We have perfectly good explicit idioms for delaying computations (eval 
and functions), to make the thunk/macro/whatever worth doing it has to 
offer something different.


> Some better examples...
> 
> * assign a bill to `a` so that `a` will evaluate to the value of the name
> `foo` any time that `a` is evaluated, in the scope of that evaluation
> 
>    a = $foo
> 
> * as above, but always plus one
> 
>    a = $foo + 1
>
> * make `a` a bill that evaluates to the value of the name `foo` at the time
> that `a` is evaluated, in that scope, plus the value of `bar` **at the time
> and in the scope of the assignment to `a`**
> 
>    a = $foo + bar

Hmmm. That implies that if you want an entire expression to have 
delayed evaluation, you have to tag everything with a sigil:

    a = $spam + $eggs - $spam*$eggs + $cheese*$spam

I think it is better to have syntax with delimiters, to turn delayed 
evaluation ON and OFF, rather than having to tag each and every 
sub-expression:

    a = `spam + eggs - spam*eggs + cheese*spam`

(disclaimer: using ` ` is just for the sake of illustration.)


> Note. Similarly to mixing floats with ints, any expression that contains a
> bill evaluates to a bill, so if `a` is a bill, `b=a+1` makes `b` a bill
> too. Passing a bill to eval should be the obvious way to get the value.
> 
> The point? It allows functions to accept bills to use internally. The
> function would specify any names the bill can reference in the function's
> API, like keywords.

Well, this contradicts your previous point. If any expression that 
contains a "bill" is a "bill", then so is func(bill). So given that, 
your example here:

> def f(b): # the bill arg `b` can reference `item`
>     for item in something:
>         if eval(b): return True
> 
> f($item < 0)

would actually need to be written as:

eval(f($item < 0))



-- 
Steven


More information about the Python-ideas mailing list