python3: 'where' keyword

  • Thread starter Andrey Tatarinov
  • Start date
A

Andrey Tatarinov

Hi.

It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
>>> res = [ f(i) for i in objects ] where:
>>> def f(x):
>>> #do something
or
>>> print words[3], words[5] where:
>>> words = input.split()

- defining variables in "where" block would restrict their visibility to
one expression

- it's more easy to read sources when you know which part you can skip,
compare to
>>> def f(x):
>>> #do something
>>> res = [ f(i) for i in objects ]

in this case you read definition of "f" before you know something about
it usage.
 
S

Steven Bethard

Andrey said:
Hi.

It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something
or
print words[3], words[5] where:
words = input.split()

- defining variables in "where" block would restrict their visibility to
one expression

How often is this really necessary? Could you describe some benefits of
this? I think the only time I've ever run into scoping problems is with
lambda, e.g.

[lambda x: f(x) for x, f in lst]

instead of

[lambda x, f=f: for x, f in lst]

Are there other situations where you run into these kinds of problems?
- it's more easy to read sources when you know which part you can skip,
compare to
def f(x):
#do something
res = [ f(i) for i in objects ]

in this case you read definition of "f" before you know something about
it usage.

Hmm... This seems very heavily a matter of personal preference. I find
that your where clause makes me skip the 'res' assignment to read what
the 'res' block contains. I had to read it twice before I actually
looked at the list comprehension. Of course, I'm sure I could be
retrained to read it the right way, but until I see some real benefit
from it, I'd rather not have to.


TOOWTDI-ily-yrs,

Steve
 
S

Steven Bethard

Steven said:
How often is this really necessary? Could you describe some benefits of
this? I think the only time I've ever run into scoping problems is with
lambda, e.g.

[lambda x: f(x) for x, f in lst]

instead of

[lambda x, f=f: for x, f in lst]

Sorry, bad example, this should have looked something more like:

[lambda y: f(x, y) for x, f in lst]

....

[lambda y, x=x, f=f: f(x, y) for x, f in lst]

where you actually need the lambda.

Steve
 
D

Donn Cave

Andrey Tatarinov wrote:
It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something
or

print words[3], words[5] where:
words = input.split()

- defining variables in "where" block would restrict their visibility to
one expression

How often is this really necessary? Could you describe some benefits of
this? I think the only time I've ever run into scoping problems is with
lambda, e.g.

[lambda x: f(x) for x, f in lst]

instead of

[lambda x, f=f: for x, f in lst]

Are there other situations where you run into these kinds of problems?

Note that he says "would be extremely useful for creating readable
sources", so the "these kinds of problems" he would have been thinking
of would be where source was not as readable as it could be. You seem
to be concerned about something else.

I don't by any means agree that this notation is worth adopting, and
in general I think this kind of readability issue is more or less a lost
cause for a language with Python's scoping rules, but the motive makes
sense to me. One way to look at it might be, if I observe that "words"
is assigned to in a where clause, then I know it will not be used
elsewhere in the surrounding scope so I can forget about it right away.
If the name does occur elsewhere, it evidently refers to something else.

Donn Cave, (e-mail address removed)
 
A

AdSR

Andrey said:
Hi.

It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something

I don't know haskell, but it looks SQL-ish to me (only by loose
association). And it's not that unpythonic - it resembles
>>> res = [x for x in sequence if x.isOk()]
or
print words[3], words[5] where:
words = input.split()

Here's a shorter version:

(Does it qualify as obfuscated Python code? :) )
- defining variables in "where" block would restrict their visibility to
one expression

- it's more easy to read sources when you know which part you can skip,

Yes, I like the readability of it, too.
compare to
def f(x):
#do something
res = [ f(i) for i in objects ]

in this case you read definition of "f" before you know something about
it usage.

When I first read your post, I thought "Well, just one more of those
Py3k ideas that appear on c.l.py every day." But as I look at the latter
example, I think you have just scratched my itch. The same thing has
bugged me more than once in my code.

I think this idea is of the same kind as the @decorator syntax. Guido
moved an operation to a point in the code where it was more visible. You
moved an operation to a more local context where (pun not intended) it
really belongs.

I'm usually rather conservative about Python syntax (@decorators,
absolute/relative imports, if-else operator), but this one could appear
in Python tomorrow and that would be too far in the future for me ;)

Cheers,

AdSR
 
P

Paul Rubin

Donn Cave said:
I don't by any means agree that this notation is worth adopting, and
in general I think this kind of readability issue is more or less a lost
cause for a language with Python's scoping rules, but the motive makes
sense to me.

But we're talking about the mythical/hypothetical Python 3, so maybe
there's a chance of fixing the scoping rules, which it seems to me are
currently pretty badly broken.
 
N

Nick Coghlan

Andrey said:
Hi.

It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something

Hmm, this is actually a really interesting idea. Avoiding accidental namespace
conflicts is certainly one of the advantages of using lambdas.

This idea has the virtue of being able to do the same thing, but have full
access to Python's function syntax and assignment statements in the 'expression
local' suite.

In fact, any subexpressions in a complicated expression can be extracted and
named for clarity without fear of screwing up the containing namespace, which
would be an enormous boon for software maintainers.

It also allows the necessary but uninteresting setup for an expression to be
moved "out of the way", bringing the expression that does the real work to
prominence.

From the interpreter's point of view, the meaning would probably be something like:

namespace = locals()
exec where_suite in globals(), namespace
exec statement in globals(), namespace
res = namespace["res"]
del namespace

Making the 'where' clause part of the grammar for the assignment statement
should be enough to make the above example parseable, too.

The clause might actually make sense for all of the simple statement forms in
the grammar which contain an expression:
expression statement
assignment statement
augmented assignment statement
del statement
print statement
return statement
yield statement
raise statement
exec statement

The clause really isn't appropriate for break, continue, import or global
statements, as they don't contain any expressions :)

For compound statements, a where clause probably isn't appropriate, as it would
be rather unclear what the where clause applied to.

Cheers,
Nick.
 
P

Paul Rubin

Nick Coghlan said:
Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something

Hmm, this is actually a really interesting idea. Avoiding accidental
namespace conflicts is certainly one of the advantages of using lambdas.

I like it too. Seems a little perl-ish, but what the hey.
In fact, any subexpressions in a complicated expression can be
extracted and named for clarity without fear of screwing up the
containing namespace, which would be an enormous boon for software
maintainers.

Sure, why not:

x = (-b + sqrt(discriminant))/(2*a) where:
discriminant = b*b - 4*a*c

Maybe you could just have a where: suite that binds variables
within the suite:

where:
def f(x):
#do something
res = [ f(i) for i in objects ]

where:
discriminant = b*b - 4*a*c
x = (-b + sqrt(discriminant))/(2*a)

Syntax is
where:
suite
statement

the suite has its own scope so any variable created there is local to
the suite plus the following statement. The scope vanishes after the
statement.
 
N

Nick Coghlan

Nick said:
It also allows the necessary but uninteresting setup for an expression
to be moved "out of the way", bringing the expression that does the real
work to prominence.

Killer app for this keyword:

class C(object):

x = property(get, set) where:
def get(self):
return "Silly property"
def set(self, val):
self.x = "Told you it was silly"

Cheers,
Nick.
 
N

Nick Coghlan

Paul said:
the suite has its own scope so any variable created there is local to
the suite plus the following statement. The scope vanishes after the
statement.

The second part of the idea is to give the statement greater prominence and
'hide' the uninteresting setup (the contents of the where clause).

Putting the statement of interest after the where suite still gives the benefits
of avoiding namespace clutter, but doesn't really help readability (it makes it
worse, if you ask me).

Particularly, what if the next statement is a compound statement?

Cheers,
Nick.
 
M

michele.simionato

But we're talking about the mythical/hypothetical Python 3, so maybe
there's a chance of fixing the scoping rules, which it seems to me are
currently pretty badly broken.

I don't think the current scoping rules will be changed in Python 3.0.
I can't give you the link right now, but there are threads about the
scope rules in
python-dev, with various people protesting and Guido saying that he
wants to
keep them as they are.

Michele Simionato
 
C

Carl Banks

Nick said:
Andrey said:
Hi.

It would be great to be able to reverse usage/definition parts in
haskell-way with "where" keyword. Since Python 3 would miss lambda, that
would be extremly useful for creating readable sources.

Usage could be something like:
res = [ f(i) for i in objects ] where:
def f(x):
#do something
[snip]
For compound statements, a where clause probably isn't appropriate, as it would
be rather unclear what the where clause applied to.

Right. But you know that as soon as you add this to simple
expressions, a bunch of people are going to come here whining about how
they don't get to use where with if-expressions.

Frankly, they might have a point here. Although we have replacing
lambda expressions on our minds, I have in mind a different problem
that a where-statement would solve perfectly. But it would have to be
used with an if-expression.

However, I think it might not be so hard. Let's take Paul Rubin's
advice and precede the if statement with where. Let's also allow
"elif" clauses to be replaced with "else where ... if" clauses. That
which is bound in the while-block would be visible in both the
if-expression and if-block.

Then we could do this:

.. where:
.. m = someregexp.match(somestring)
.. if m:
.. blah blah blah
.. else where:
.. m = someotherregexp.match(somestring)
.. if m:
.. blah blah blah

We might want to spell "else where" instead as "elwhere", to match
"elif", but that's not important now. This would take away one of the
major minor annoyances of Python. (In fact, I've suggested something
like this as a solution to the set-and-test idiom, which Python makes
difficult, only I used the keyword "suppose" instead of "where".)

Ok, but if you do that, now you have people whining that "where" comes
after some expressions, and before others. (This would not bother me
one bit, BTW, but I'm pretty sure I'd lose the popular vote on this
one.)

So, let's go all out and say that while could precede any statement.
We now have consistency. Well, that really wouldn't work for the
if-statement, though, because then how could we apply a different
while-block to an else clause? We'd have to treat if-statements
specially anyways. So we don't have consistency.

My solution would be to propose two different where statements: a
where...do statement, and a separate where...if statement. The
where...do statement would look like this:

.. where:
.. def whatever(): pass
.. do:
.. blah blah use whatever blah

It has the advantage of being able to apply the where bindings to
several statements, and is, IMO, much cleaner looking than simply
applying where's bindings to the single following unindented statement.

I would recommend against where...while and where...for statements.
They can't accomplish anything you couldn't do with a break statement
inside the block, and it's not obvious whether the where clause gets
executed once or for each loop (since it's physically outside the loop
part).

One question: what do you do with a variable bound inside a while-block
that has the same name as a local variable? (Or, horrors, a
surrounding while-block?) I'm inclined to think it should be illegal,
but maybe it would be too restrictive.
Anyways, I like this idea a lot.

+1
 
N

Nick Coghlan

Carl said:
Right. But you know that as soon as you add this to simple
expressions, a bunch of people are going to come here whining about how
they don't get to use where with if-expressions.

Frankly, they might have a point here. Although we have replacing
lambda expressions on our minds, I have in mind a different problem
that a where-statement would solve perfectly. But it would have to be
used with an if-expression.

I have a different suggestion for this.

'as' is used for renaming in import statements. 'as' will be used for exception
naming in Python 3k.

So let's use it for expression naming in 'if' statements, too.

if someregexp.match(s) as m:
# blah using m
elif someotherregexp.match(s) as m:
# blah using m

Cheers,
Nick.
 
P

Paul Rubin

Nick Coghlan said:
So let's use it for expression naming in 'if' statements, too.

if someregexp.match(s) as m:
# blah using m
elif someotherregexp.match(s) as m:
# blah using m

Certainly an improvement over what we have now.
 
A

AdSR

Nick said:
Killer app for this keyword:

class C(object):

x = property(get, set) where:
def get(self):
return "Silly property"
def set(self, val):
self.x = "Told you it was silly"

Hey, this is super-elegant!

AdSR
 
B

Bengt Richter

Killer app for this keyword:

class C(object):

x = property(get, set) where:
def get(self):
return "Silly property"
def set(self, val):
self.x = "Told you it was silly"

Yes, that is cool and it _is_ an interesting idea. Are suites nestable? E.g., is this legal?

x = term1 + term2 where:
term1 = a*b where:
a = 123
b = 456
term2 = math.pi

Reminds me of some kind of weird let ;-)

And, is the whole thing after the '=' an expression? E.g.,

x = ( foo(x) where:
x = math.pi/4.0
) where:
def foo(x): print 'just for illustration', x

or is this legal?

for y in ([foo(x) for x in bar] where:
bar = xrange(5)
): baz(y) where:
def baz(arg): return arg*2

Not trying to sabotage the idea, really, just looking for clarification ;-)

Regards,
Bengt Richter
 
A

Andrey Tatarinov

Bengt said:
Killer app for this keyword:

class C(object):

x = property(get, set) where:
def get(self):
return "Silly property"
def set(self, val):
self.x = "Told you it was silly"
Yes, that is cool and it _is_ an interesting idea. Are suites nestable? E.g., is this legal? ....
And, is the whole thing after the '=' an expression? E.g.,

x = ( foo(x) where:
x = math.pi/4.0
) where:
def foo(x): print 'just for illustration', x

or is this legal?

for y in ([foo(x) for x in bar] where:
bar = xrange(5)
): baz(y) where:
def baz(arg): return arg*2

Not trying to sabotage the idea, really, just looking for clarification ;-)

yes, all your examples are correct. And that's the way I'd like to use
this feature.
 
A

Andrey Tatarinov

Nick said:
Killer app for this keyword:

class C(object):

x = property(get, set) where:
def get(self):
return "Silly property"
def set(self, val):
self.x = "Told you it was silly"

oh, that's great! I can't imagine prettier example
 
?

=?iso-8859-1?B?QW5kcuk=?=

At the risk of generating controversy, here's another type of example:

def gcd(a, b):
where:
a: int, b: int
return c where:
c: int
while a:
a, b = b%a, a
return b

More can be found at http://aroberge.blogspot.com

Andre
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top