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

Steven D'Aprano steve at pearwood.info
Wed Mar 5 16:35:29 CET 2014


On Wed, Mar 05, 2014 at 12:41:10PM +0000, Carl Smith wrote:
> Just for the record, I never called them bills just because they contain a
> dollar sign! I was thinking of a short word which describes an expression
> who's evaluation changes depending on the local circumstances. Like a legal
> bill, not a dollar bill.

Hmmm. Well, I don't really get the connection between legal bills and 
delayed evaluation, and I still really dislike the name.


> The name macro doesn't really work, as it's only an expression, it's not a
> code block.

There is nothing about macros that *require* them to accept a full block 
of code. In fact, there are multiple different meanings for macro, 
although they are all related they do have significant differences:

http://en.wikipedia.org/wiki/Macro_%28computer_science%29

Macros in C are not the same kind of thing as macros in Lisp.


> ---
> 
> The idea was never to start passing complex expressions around the place.

What you consider a complex expression somebody else may consider a 
simple expression. Unless you want to demand some arbitrary hard limit 
on complexity (and how do you measure complexity?) this "bill" system 
would have to allow the exact same types of expressions that are legal 
elsewhere in Python.


[...]
> What the hell is a thunk anyway? It's a horrible name.

Wikipedia is your friend:
http://en.wikipedia.org/wiki/Thunk

Also, more here: http://c2.com/cgi/wiki?CallByName

The meaning of thunk I'm referring to comes from the call-by-name 
argument passing model. Suppose we pass an expression to a function:

    function(arg=x-1)

and the body of the function looks like this:

    if arg > 1: 
        y = arg + 1
    else: 
        y = arg - 1
    return y*arg

In the "call-by-name" model, the function body executes like this:

    if (x+1) > 1: 
        y = (x+1) + 1
    else: 
        y = (x+1) - 1
    return y*(x+1)

which not only duplicates the "x+1" part four times, but may require 
evaluating it three times. To avoid this wasteful duplication, the 
compiler creates a special thunk value, which is something like a 
function with no arguments:

    def thunk():
        if cache is None:
            cache = x-1
        return cache

(This should be considered pseudo-code, not the exact way Algol or 
Haskell work -- real thunks also allow you to assign a value back to the 
"name". Also, technically the presence of a cache makes it call-by-need 
rather than call-by-name).

The body of the function then becomes:

    if thunk() > 1: 
        y = thunk() + 1
    else: 
        y = thunk() - 1
    return y*thunk()


So as you can see, what I've been calling a thunk is not precisely like 
Algol thunks. One thing which is missing is the dynamic scoping: in the 
thunk evaluation, the x comes from the caller's scope. But the delayed 
evaluation part is quite similar, enough that I think the name may be 
appropriate. Another name for this might be a "promise", as in the 
promise to execute a computation later.


-- 
Steven


More information about the Python-ideas mailing list