[Python-ideas] Where-statement (Proposal for function expressions)

Chris Perkins chrisperkins99 at gmail.com
Thu Jul 16 16:51:54 CEST 2009


On Thu, Jul 16, 2009 at 9:39 AM, Gerald Britton<gerald.britton at gmail.com> wrote:
>
> d = chain(g1, g2) where:
>         g1 = (i for i in [2,3,5])
>         g1 = (j for j in [7,11])
>
> Even though there are just as many parentheses, they're broken out and
> easier to spot.  Sure, you could define g1 and g2 ahead of time, but
> if the "where" clause creates a new namespace like a function def in a
> function, then g1 and g2 are just placeholders in the expression and
> don't pollute the variable namespace of the calling context.

This idea is really growing on me.

Here's another example - how do you calculate sample variance? You're
probably thinking "Um, well, I think it's the square root of the sum
of the squares minus the square of the sums". Great, that's a high-ish
level, intuitive description of the algorithm. The precise details of
how to go about calculating that are not important yet. So write it
that way:

def variance(data):
    return sqrt(sum_of_squares - square_of_sums) where:
        ... elided ...

There - we've written the core of the algorithm at the same level of
abstraction as we think about it. The code matches our mental model.
Only then do we go about filling in the boring details.

def variance(data):
    return sqrt(sum_of_squares - square_of_sums) where:
        def sqrt(v): return v ** 0.5
        sum_of_squares = sum(v**2 for v in data)
        square_of_sums = sum(data) ** 2

The advantage is that now readers of the code, having developed some
intuition about what "where blocks" mean, can see at a glance that the
code at the first level of indentation is the important stuff, and the
indented block is the implementation details; the stuff that's at a
lower level of abstraction.

I think this construct would be a great way to enable SLAP - the Same
Level of Abstraction Principle (which I'm embarrasingly in love with).
Traditional SLAP says that all the code in a function should be at the
same level of abstraction - code at a lower level of abstraction
should be split out into other functions or methods. A refinement
could could state that code at a lower level of abstraction should be
either moved to another function, or indented in a where block.

> Anyway, I don't feel strongly either way.  I see the benefits of
> "where" clauses and I'd use them if they were available, but I can
> certainly do without.

I would use them too, and I can't do without. I'm going to hold my
breath until we get them. ;)

As for the precise specification, I see only a few issues.

As a first approximation, assume that this code:

[LHS OP] EXPR where:
    BODY

is expanded into the equivalent of this:

def TEMP():
    BODY
    return EXPR
[LHS OP] TEMP()

That seems like the most obvious way to get a nested scope to run the body in.

Problem 1) return
Since there will be an implicit return injected into the body of TEMP,
what happens if the user puts an explicit return in the body of the
block:
    x = foo(y) where:
        return 23
expands to
    def TEMP():
        return 23
        return foo(y)
    x = TEMP()
So return should be disallowed.

Problem 2) break/continue
    for a in range(b):
        x = foo(y) where:
            break #or continue
expands to:
    for a in range(b):
        def TEMP():
            break # or continue
            return foo(y)
        x = TEMP()
Which is an error. However:
    x = foo(y) where:
        y = []
        for i in range(n):
            if cond(i):
                break
            y.append(i)
must be allowed (right?). So we can't make a blanket restriction on
break/continue within the body.

Problem 3) yield
    x = foo(y) where:
        yield 3
expands to:
    def TEMP():
        yield 3
        return foo(y)
    x = TEMP()
which is an error. So yield must be disallowed.

I can't think of any other constructs that would need special
handling, and I'm sure the issues I have listed are solvable.


Chris Perkins



More information about the Python-ideas mailing list