Allowing breaks in generator expressions by overloading the while keyword
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example. Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues. Besides, overloading `while` is much cuter... ls = [ expr for name in iterable while expr ] ls = [ expr for name in iterable if expr while expr ] ls = [ expr for name in iterable if expr else expr while expr ] With regular generator expressions, it gives you a kind of base case, which may assist with showing off.
Am 2014-02-21 03:13, schrieb Carl Smith:
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example.
Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues. Besides, overloading `while` is much cuter...
ls = [ expr for name in iterable while expr ]
ls = [ expr for name in iterable if expr while expr ]
ls = [ expr for name in iterable if expr else expr while expr ]
With regular generator expressions, it gives you a kind of base case, which may assist with showing off.
I think this was already suggested once on this list. See: https://groups.google.com/forum/#!topic/python-ideas/4O1TCOa2fo8
This seems *so* close to what itertools.takewhile() does that it's really hard for me to see why we need special syntax for it. On Thu, Feb 20, 2014 at 6:13 PM, Carl Smith <carl.input@gmail.com> wrote:
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example.
Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues. Besides, overloading `while` is much cuter...
ls = [ expr for name in iterable while expr ]
ls = [ expr for name in iterable if expr while expr ]
ls = [ expr for name in iterable if expr else expr while expr ]
With regular generator expressions, it gives you a kind of base case, which may assist with showing off.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On Feb 20, 2014, at 22:47, David Mertz <mertz@gnosis.cx> wrote:
This seems *so* close to what itertools.takewhile() does that it's really hard for me to see why we need special syntax for it.
Nick Coghlan made the same observation last time around. The counter is that if clauses in comprehensions are just as close to what filter() does and we have special syntax for them. But I think that's a false analogy. I wrote up a blog post at http://stupidpythonideas.blogspot.com/2013/07/syntactic-takewhile.html that you might find interesting. It's also worth looking at the other already-working alternatives from the thread, like putting a StopIteration-raising function in the expression or a higher if clause. Also, variations of the idea have come up multiple times before that, and in 2009 one of them was written up as PEP 3142 to be officially rejected.
On Thu, Feb 20, 2014 at 6:13 PM, Carl Smith <carl.input@gmail.com> wrote:
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example.
Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues. Besides, overloading `while` is much cuter...
ls = [ expr for name in iterable while expr ]
ls = [ expr for name in iterable if expr while expr ]
ls = [ expr for name in iterable if expr else expr while expr ]
With regular generator expressions, it gives you a kind of base case, which may assist with showing off.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Cheers for all your input, and sorry for suggesting something stale. I'll pick it up in the other thread. I'll read the blog first too. Thanks again.
On Fri, Feb 21, 2014 at 02:13:13AM +0000, Carl Smith wrote:
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example.
Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues.
The main issue being that it gives a SyntaxError :-)
Besides, overloading `while` is much cuter...
ls = [ expr for name in iterable while expr ]
I would *love* this syntax. I've read the various arguments against it, and they don't convince me. It is functionality which is often needed and requested, e.g. http://stackoverflow.com/questions/5505891/using-while-in-list-comprehension... Clojure has list comprehensions that behave this way: http://clojuredocs.org/clojure_core/clojure.core/for but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen. -- Steven
On Feb 21, 2014, at 2:24, Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, Feb 21, 2014 at 02:13:13AM +0000, Carl Smith wrote:
Sometimes you need to build a list in a loop, but break from the loop if some condition is met, keeping the list up to that point. This is so common it doesn't really need an example.
Trying to shoehorn the break keyword in to the generator expression syntax doesn't look pretty, unless you add `and break if expr` to the end, but that has its own issues.
The main issue being that it gives a SyntaxError :-)
Besides, overloading `while` is much cuter...
ls = [ expr for name in iterable while expr ]
I would *love* this syntax. I've read the various arguments against it, and they don't convince me. It is functionality which is often needed and requested, e.g.
http://stackoverflow.com/questions/5505891/using-while-in-list-comprehension...
Clojure has list comprehensions that behave this way:
As I pointed out in the thread last summer (and briefly mentioned in the blog post linked earlier in this thread), that's because Clojure comprehensions are not actually the same as Python comprehensions. Python comprehensions, as borrowed from Haskell, have a free sequence of clauses that nest from left to right. There is no sensible way to interpret a while clause that nests under a for or if clause. Clojure-style comprehensions, as borrowed from (I think) Racket only have nested loops, no other clauses--but then each loop has modifiers. Instead of if clauses, you have a "filter" that modifies a loop. You can't have a while clause, but you can have a "takewhile" that modifies a loop. You get a clean, simple, and easy-to-extend syntax. This has the down side that you can't toss two if conditions into a single loop, but that almost never matters (just and them together) It has the up side that new modifiers don't have to nest under arbitrary clauses, they just have to modify a loop. In Haskell, because it's so trivial to modify the iterable itself (e.g., with a takewhile whose predicate is composed on the fly), there really is no cost to using the Haskell-style comprehension. Sadly, that isn't true in Python, so the limitation hits us harder. Can we get from here to there? Well, it might be possible to come up with a syntax for Clojure-style comprehensions that handles all currently valid Python comprehensions without two adjacent if clauses with the same meaning, despite getting there by a slightly different route. If so, just deprecate adjacent if clauses, then switch to the syntax and semantics where if is a modifier on the for clause instead of a separate clause and adjust the docs accordingly, then add while as another modifier. Or, it would almost certainly be possible to design a hybrid, where while is a loop modifier on the for clause rather than a nested clause, but if stays the way it is today. And if you go that way, it probably makes sense to add the same modifier onto the for statement. Then all of the docs that explain comprehensions could remain unchanged. And no need for a deprecation period or removing adjacent ifs. This may sound more radical than "just add a while clause", but given that the latter isn't actually a sensible proposal in the first place, you're not going to be able to flesh it out in a way that gets Nick Coghlan to reconsider; the former, you might have a shot at.
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
You could always just fork Python instead of revolting. ;)
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :) However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions. Re-reading Andrew's post at http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html, I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator. Consider: isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen)) Is there are a nicer way to write that? The Haskell equivalent is: (< n) . (** 2) That's not very readable to most Python programmers, but what if you could write something like: isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen)) This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences: 1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments. However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343). Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 21 February 2014 22:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Oh, and under such a proposal, the generator expression: (x for x in seq) would be semantically equivalent to: (: yield x for x in ?)(seq) Currently, there's no underlying construct you can decompose a generator expression into, because there's no notation for a lambda expression with an anonymous parameter. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 21.02.2014 13:25, Nick Coghlan wrote:
On 21 February 2014 22:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Oh, and under such a proposal, the generator expression:
(x for x in seq)
would be semantically equivalent to:
(: yield x for x in ?)(seq)
Currently, there's no underlying construct you can decompose a generator expression into, because there's no notation for a lambda expression with an anonymous parameter.
Hmm, this reminds me too much of regular expression syntax :-) I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect. Programs don't get faster that way, they don't get more readable, you don't get to do more things that couldn't do otherwise and requiring a master in computer science to be able to understand what goes on in one of those magical lines doesn't feel right to me either, given that we are promoting Python as first programming language. Of course, tossing around ideas like these is fun and I don't want to spoil it. Eventually something useful will come out of these discussions, I'm sure :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Feb 21 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-02-12: Released mxODBC.Connect 2.0.4 ... http://egenix.com/go53 ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 2014-02-21, at 14:03 , M.-A. Lemburg <mal@egenix.com> wrote:
On 21.02.2014 13:25, Nick Coghlan wrote:
On 21 February 2014 22:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Oh, and under such a proposal, the generator expression:
(x for x in seq)
would be semantically equivalent to:
(: yield x for x in ?)(seq)
Currently, there's no underlying construct you can decompose a generator expression into, because there's no notation for a lambda expression with an anonymous parameter.
Hmm, this reminds me too much of regular expression syntax :-)
I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect.
"Line" is a red herring, I think. Nobody *really* cares about lines. "Expression" is the important factor. Moving stuff out of expressions and into separate statements requires finding out a name for the result, is generally significantly more verbose and isn't necessarily clearer. Depending on the situation, it may also require significant reworking of existing (supposedly working) code. I usually write non-trivial generator expressions over multiple lines, I may be after them remaining a single (readable) expression depending on context. Well that's just my experience anyway.
Programs don't get faster that way, they don't get more readable, you don't get to do more things that couldn't do otherwise and requiring a master in computer science to be able to understand what goes on in one of those magical lines doesn't feel right to me either, given that we are promoting Python as first programming language.
Was the MSCS insult really necessary?
Of course, tossing around ideas like these is fun and I don't want to spoil it. Eventually something useful will come out of these discussions, I'm sure :-)
2014-02-21 15:34 GMT+02:00 Masklinn <masklinn@masklinn.net>:
On 2014-02-21, at 14:03 , M.-A. Lemburg <mal@egenix.com> wrote:
On 21.02.2014 13:25, Nick Coghlan wrote: I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect.
"Line" is a red herring, I think. Nobody *really* cares about lines.
People do. There are only a handful of them in the screen. And the eyes catch even less in a single glance. --- Elazar
2014-02-21 15:34 GMT+02:00 Masklinn <masklinn@masklinn.net>:
On 2014-02-21, at 14:03 , M.-A. Lemburg <mal@egenix.com> wrote:
On 21.02.2014 13:25, Nick Coghlan wrote: I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect.
"Line" is a red herring, I think. Nobody *really* cares about lines.
People do. There are only a handful of them in the screen. And the eyes catch even less in a single glance.
But on the contrary, trying to compress control flow in a single line breaks the visual structure expectation for Python code (in which most control flow is introduced by a colon and a linefeed). Regards Antoine.
Antoine Pitrou wrote:
But on the contrary, trying to compress control flow in a single line breaks the visual structure expectation for Python code (in which most control flow is introduced by a colon and a linefeed).
We've had control flow in expressions for a long time. The 'and' and 'or' operators affect control flow, but nobody complains that they "break the visual flow". Quite the opposite: they're useful because they *maintain* the visual flow. They allow the code to be read declaratively without thinking in terms of control flow. The same thing applies to the if-expression, and comprehensions, and the proposed except-expression. They're attractive because they expand the range of code that can be written in a declarative rather than an imperative style. Often they allow something to be written on a single line that would otherwise require multiple lines, but that's not the only reason for using them. In fact, I would say that using them *just* to save space is often a bad idea -- if the code isn't easier to read when written declaratively, then you shouldn't be writing it that way, even if it is shorter. -- Greg
On Sat, 22 Feb 2014 11:58:40 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Antoine Pitrou wrote:
But on the contrary, trying to compress control flow in a single line breaks the visual structure expectation for Python code (in which most control flow is introduced by a colon and a linefeed).
We've had control flow in expressions for a long time. The 'and' and 'or' operators affect control flow, but nobody complains that they "break the visual flow".
You can easily think of them as shortcutting operators. Doing the same with "break" isn't possible. Regards Antoine.
On 21.02.2014 14:34, Masklinn wrote:
On 2014-02-21, at 14:03 , M.-A. Lemburg <mal@egenix.com> wrote:
On 21.02.2014 13:25, Nick Coghlan wrote:
On 21 February 2014 22:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Oh, and under such a proposal, the generator expression:
(x for x in seq)
would be semantically equivalent to:
(: yield x for x in ?)(seq)
Currently, there's no underlying construct you can decompose a generator expression into, because there's no notation for a lambda expression with an anonymous parameter.
Hmm, this reminds me too much of regular expression syntax :-)
I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect.
"Line" is a red herring, I think. Nobody *really* cares about lines. "Expression" is the important factor. Moving stuff out of expressions and into separate statements requires finding out a name for the result, is generally significantly more verbose and isn't necessarily clearer.
But it helps a lot in debugging the code :-) Debugging complicated expressions becomes really difficult and adding a few local variables to put on the watch list helps a lot.
Depending on the situation, it may also require significant reworking of existing (supposedly working) code.
I usually write non-trivial generator expressions over multiple lines, I may be after them remaining a single (readable) expression depending on context.
Well that's just my experience anyway.
Programs don't get faster that way, they don't get more readable, you don't get to do more things that couldn't do otherwise and requiring a master in computer science to be able to understand what goes on in one of those magical lines doesn't feel right to me either, given that we are promoting Python as first programming language.
Was the MSCS insult really necessary?
Which "insult" ? I have no intention insulting anyone and apologize if the above was interpreted that way. I was just referring to the complicated nature of the resulting expressions and raising a concern that by introducing more constructs to write such things, we actively support writing Python programs which are no longer easy to read.
Of course, tossing around ideas like these is fun and I don't want to spoil it. Eventually something useful will come out of these discussions, I'm sure :-)
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Feb 21 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-02-12: Released mxODBC.Connect 2.0.4 ... http://egenix.com/go53 ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 21 February 2014 23:03, M.-A. Lemburg <mal@egenix.com> wrote:
On 21.02.2014 13:25, Nick Coghlan wrote:
On 21 February 2014 22:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
This is somewhat similar to the implicit lambda proposal in http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Oh, and under such a proposal, the generator expression:
(x for x in seq)
would be semantically equivalent to:
(: yield x for x in ?)(seq)
Currently, there's no underlying construct you can decompose a generator expression into, because there's no notation for a lambda expression with an anonymous parameter.
Hmm, this reminds me too much of regular expression syntax :-)
I wonder why people are so keen on stuffing too much logic into a single line. Must be a twitter/SMS side-effect.
Programs don't get faster that way, they don't get more readable, you don't get to do more things that couldn't do otherwise and requiring a master in computer science to be able to understand what goes on in one of those magical lines doesn't feel right to me either, given that we are promoting Python as first programming language.
Of course, tossing around ideas like these is fun and I don't want to spoil it. Eventually something useful will come out of these discussions, I'm sure :-)
The reason I keep beating my head against this particular wall (cf. PEP 403's @in clauses and PEP 3150's given suites) is that my personal goal for Python is that it should be a tool that lets people express what they are thinking clearly and relatively concisely. As far as I have been able to tell, the persistent requests for "multi-line lambdas", cleaner lambda syntax, etc, are because Python doesn't currently make it easy to express a lot of operations that involve higher order manipulation of "one shot" callables - closures or custom functions where you *don't* want to re-use them, but Python still forces you to pull them out and name them. I think PEP 403 is my best current description of the mental speed bump involved: http://www.python.org/dev/peps/pep-0403/ Decorators are an example where this used to be a problem (compounded by other issues related to repeating the name multiple times): def f(cls): ... f = classmethod(f) But this was replaced by the much cleaner: @classmethod def f(cls): ... Like try/except/finally -> with statements, decorators replaced a "bracketed" construct with a "prefix" only construct, greatly improving the readability in the process. Extracting a named one-shot function inline is problematic in much the same way: def custom_key(v): # Helper for a sorted call you don't know is coming yet ... x = sorted(seq, key=custom_key) # Oh, that's what it is for The idea behind both PEP 403 and 3150 is to be able to do a similar bracketed -> prefix only transformation for more constructs where the callable is incidental to the real operation. The PEP 403 approach to custom key functions: @in x = sorted(seq, key=custom_key) def custom_key(v): ... I'm not actually happy with the PEP 403 syntax (or the complexity of PEP 3150), but I think the problem they both attempt to solve is one worth having a better answer for. I just haven't figured out what that answer might look like yet (and neither has anyone else) :P Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
From: Nick Coghlan <ncoghlan@gmail.com> Sent: Friday, February 21, 2014 6:17 AM
The reason I keep beating my head against this particular wall (cf. PEP 403's @in clauses and PEP 3150's given suites) is that my personal goal for Python is that it should be a tool that lets people express what they are thinking clearly and relatively concisely.
As far as I have been able to tell, the persistent requests for "multi-line lambdas", cleaner lambda syntax, etc, are because Python doesn't currently make it easy to express a lot of operations that involve higher order manipulation of "one shot" callables - closures or custom functions where you *don't* want to re-use them, but Python still forces you to pull them out and name them. I think PEP 403 is my best current description of the mental speed bump involved: http://www.python.org/dev/peps/pep-0403/
There are actually two different—in fact, nearly opposite—problems that get lumped together. The first problem is that you often want to use _expressions_ as values, and you can't. The second problem is that you sometimes want to build complicated functions in-line. Solving that does nothing to help the first problem (as JavaScript proves), and I'm not sure it's really as big a problem as people think (there's a reason experienced JavaScript programmers start using Deferreds/Promises—borrowed from Python—to escape the "callback hell" their language allows), but I won't get into that here. The fact that both of these manifest as "problems with lambda" is really just a coincidence—the first is a problem with lambda because the only way we have to simulate passing around expressions is by wrapping them in functions; the second is a problem with lambda because without statements inside expressions it's the only way to define a function inline. Anyway, the first problem is why we have comprehensions, and why people want more out of them. What can comprehensions do that map and filter can't? Maybe they're a little faster, maybe they let you write long sequences of map and filter in order instead of mutated into prefix order, but nobody cares about either of those enough to add new syntax. The real benefit is that they let you map an expression over an iterable, or filter an iterable with an expression, without wrapping it upon a function. Compare: [x**2 for x in seq] map(lambda x: x**2, seq) The second one forces the reader to think through an extra abstraction that isn't relevant to the problem. And people want the same thing more generally. Most novices don't think of callbacks as higher-order functions, which is why they write this: b = Button("button 10", callback=onclick(10)) … instead of one of these: b = Button("button 10", callback=lambda: onclick(10)) b = Button("button 10", callback=partial(onlick, 10)) But really, why should they have to? All they want to say is "when I click the button, evaluate the expression onclick(10)". Why is that so hard? It's not really about improving lambda, but about removing the need for it. Surely this isn't any easier to explain: b = Button("button 10", callback=:onclick(10)) Even if it isn't a complete solution, there is of course an obvious practical benefit in readability. If you see a chain of widget declarations, and they all end with something like "callback=:onclick(10)", you only really have to process that ":" once for the whole chain, and then it fades into the background as an unimportant detail in understanding the real point of the code. So I love the idea. And, so far, Python has been adding practical partial solutions. As you (Nick) pointed out, comprehensions, decorators, with statements, conditional and except expressions, etc. are all ways to provide new contexts where expressions can be used without wrapping them in functions. There are probably a few others worth covering, and at that point maybe we'll have handled 90% of the cases people need, and they can deal with the last 10%. While that may sound clunky, I'm not sure it's really a problem. Python isn't a perfect language, but it's a very good language, and that's why we all use it. Of course if there's a general solution, that would be great. But I'm not sure there is. Lisp-style quoting and macros, or Haskell-style higher-order-functioning, _might_ be general solutions—but even if they are, I'm not sure they're solutions that fit into Python. (Still, for the first two, everyone really should play with MacroPy and get a feel for what it can do and how Pythonic it can feel…) I wrote this up in more detail, with a lot more digressions, as a blog post, but blogger.com seems to be having problems, so the world is spared my rambling. :)
Nick Coghlan wrote:
Oh, and under such a proposal, the generator expression:
(x for x in seq)
would be semantically equivalent to:
(: yield x for x in ?)(seq)
I don't follow that, since 'yield x for x in y' is not currently a legal expression. -- Greg
On 21 February 2014 12:18, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
I think the two are very similar. PEP 463 proposes that you could do item = stuff['name'] except KeyError: 'default' which you can already do in an ugly sort of way with a helper function: item = catchdefault(lambda: stuff['name'], KeyError, 'default') This proposal is that you could do: isprime = all(n % p for p in primes_seen while p ** 2 <= n) which you can already do in an ugly sort of way with a helper function isprime = all(n % p for p in takewhile(lambda p: p**2 <= n, primes_seen)) Neither syntax proposal gives anything that fundamentally can't be done with helpers and lambda functions. Both syntax ideas are about finding an intuitive and readable way of representing commonly used patterns without too much visual clutter. Similarly in either case you could just refactor the ugly part out into a different function. But splitting code up into different functions also comes with a readability cost. Do you think that the following is any clearer than either of the previous two? isprime = all(n % p for p in take_le_sqrt(primes_seen, n)) Oscar
From: Nick Coghlan <ncoghlan@gmail.com> Sent: Friday, February 21, 2014 4:18 AM
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html, I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers
That's really not a shorthand for lambda, it's a combination of other features that make lambdas often unnecessary. I think the C++ boost::lambda example is a better fit: _1 ** 2 < n
but what if you
could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
I like this. The ? is a lot better than the _ or _1 I suggested (especially to people who do a lot of SQL programming in Python and already use ? as an inline parameter), and using a colon makes it explicit without adding too much visual noise. The only concern is whether using up one of the handful of as-yet-unused ASCII symbols raises the bar a little too high…
This is somewhat similar to the implicit lambda proposal in
http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Would it be worth extending this to multiple arguments? I think it would be handy, but I can't think of any way to make it look nearly as good as the 0- and 1-arg forms. Obviously this should still be a one-arg lambda that just uses its parameter repeatedly: : ? * x**2 + ? * x + ? Sticking a number on the ? looks terrible: : ?1 * x**2 + ?2 * x + ?3 Looking at the other DB-API 2.0 param styles, this is hideous because of the way it uses colons twice: : :1 * x**2 + :2 * x + :3 Something like this wouldn't be too bad, except that it already has a much more useful meaning, set literals: : {1} * x**2 + {2} * x + {3} So really, the best I can come up with is the boost::lambda version I already didn't like and you already dismissed without comment: : _1 * x**2 + _2 * x + _3 On the other hand, any expression this complex—even with just one parameter—is already starting to look pretty bad as an implicit lambda, and the downsides of an explicit lambda go down the longer it gets. So maybe the best answer is: lambda a, b, c: a * x**2 + b * x + c Anyway, we can always start with just 0- and 1-argument forms and come back to expand the idea later, right?
However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343).
Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define.
I'll start digging up some examples; it is hard to predict how nice this will look until we see it in a wide range of realistic code.
On Fri, Feb 21, 2014 at 2:34 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Nick Coghlan <ncoghlan@gmail.com>
Sent: Friday, February 21, 2014 4:18 AM
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at
http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html,
I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers
That's really not a shorthand for lambda, it's a combination of other features that make lambdas often unnecessary. I think the C++ boost::lambda example is a better fit:
_1 ** 2 < n
but what if you
could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
I like this. The ? is a lot better than the _ or _1 I suggested (especially to people who do a lot of SQL programming in Python and already use ? as an inline parameter), and using a colon makes it explicit without adding too much visual noise. The only concern is whether using up one of the handful of as-yet-unused ASCII symbols raises the bar a little too high...
This is somewhat similar to the implicit lambda proposal in
http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Would it be worth extending this to multiple arguments? I think it would be handy, but I can't think of any way to make it look nearly as good as the 0- and 1-arg forms.
Obviously this should still be a one-arg lambda that just uses its parameter repeatedly:
: ? * x**2 + ? * x + ?
Sticking a number on the ? looks terrible:
: ?1 * x**2 + ?2 * x + ?3
Looking at the other DB-API 2.0 param styles, this is hideous because of the way it uses colons twice:
: :1 * x**2 + :2 * x + :3
Something like this wouldn't be too bad, except that it already has a much more useful meaning, set literals:
: {1} * x**2 + {2} * x + {3}
So really, the best I can come up with is the boost::lambda version I already didn't like and you already dismissed without comment:
: _1 * x**2 + _2 * x + _3
On the other hand, any expression this complex--even with just one parameter--is already starting to look pretty bad as an implicit lambda, and the downsides of an explicit lambda go down the longer it gets. So maybe the best answer is:
lambda a, b, c: a * x**2 + b * x + c
Anyway, we can always start with just 0- and 1-argument forms and come back to expand the idea later, right?
However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343).
Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define.
I'll start digging up some examples; it is hard to predict how nice this will look until we see it in a wide range of realistic code. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
+1 for the question mark. However, there's the problem of multi-argument lambdas. The ?(number) looks to much like Perl. What about something like ? being an array of arguments? -- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
From: Ryan Gonzalez <rymg19@gmail.com> Sent: Friday, February 21, 2014 1:36 PM
On Fri, Feb 21, 2014 at 2:34 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Nick Coghlan <ncoghlan@gmail.com>
Sent: Friday, February 21, 2014 4:18 AM
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html, I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers
That's really not a shorthand for lambda, it's a combination of other features that make lambdas often unnecessary. I think the C++ boost::lambda example is a better fit:
_1 ** 2 < n
but what if you
could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
I like this. The ? is a lot better than the _ or _1 I suggested (especially to people who do a lot of SQL programming in Python and already use ? as an inline parameter), and using a colon makes it explicit without adding too much visual noise. The only concern is whether using up one of the handful of as-yet-unused ASCII symbols raises the bar a little too high…
This is somewhat similar to the implicit lambda proposal in
http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Would it be worth extending this to multiple arguments? I think it would be handy, but I can't think of any way to make it look nearly as good as the 0- and 1-arg forms.
Obviously this should still be a one-arg lambda that just uses its parameter repeatedly:
: ? * x**2 + ? * x + ?
Sticking a number on the ? looks terrible:
: ?1 * x**2 + ?2 * x + ?3
Looking at the other DB-API 2.0 param styles, this is hideous because of the way it uses colons twice:
: :1 * x**2 + :2 * x + :3
Something like this wouldn't be too bad, except that it already has a much more useful meaning, set literals:
: {1} * x**2 + {2} * x + {3}
So really, the best I can come up with is the boost::lambda version I already didn't like and you already dismissed without comment:
: _1 * x**2 + _2 * x + _3
On the other hand, any expression this complex—even with just one parameter—is already starting to look pretty bad as an implicit lambda, and the downsides of an explicit lambda go down the longer it gets. So maybe the best answer is:
lambda a, b, c: a * x**2 + b * x + c
Anyway, we can always start with just 0- and 1-argument forms and come back to expand the idea later, right?
However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343).
Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define.
I'll start digging up some examples; it is hard to predict how nice this will look until we see it in a wide range of realistic code. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
+1 for the question mark. However, there's the problem of multi-argument lambdas. The ?(number) looks to much like Perl. What about something like ? being an array of arguments?
Doesn't that just make it even _more_ like perl, where arguments are always passed as an array? Anyway, presumably you mean that this: lambda *args: args[0] * x**2 + args[1] * x + args[2] … could be written as: : ?[0] * x**2 + ?[1] * x + ?[2] I think that's horribly unreadable—partly because of the use of *args in the first place, and partly because ?[0] looks too… perlesque. I think the best answer is the one I suggested above—and which I think Nick implicitly assumed because he's smart enough to get there directly: At least initially, just do 0-argument and 1-argument lambdas. As long as you leave the door open for extended syntaxes in a future proposal, we don't need to figure them out yet.
On Fri, Feb 21, 2014 at 4:02 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Ryan Gonzalez <rymg19@gmail.com> Sent: Friday, February 21, 2014 1:36 PM
On Fri, Feb 21, 2014 at 2:34 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Nick Coghlan <ncoghlan@gmail.com>
Sent: Friday, February 21, 2014 4:18 AM
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have
that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at
http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html,
I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers
That's really not a shorthand for lambda, it's a combination of other features that make lambdas often unnecessary. I think the C++ boost::lambda example is a better fit:
_1 ** 2 < n
but what if you
could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n),
ruled primes_seen))
I like this. The ? is a lot better than the _ or _1 I suggested
(especially to people who do a lot of SQL programming in Python and already use ? as an inline parameter), and using a colon makes it explicit without adding too much visual noise. The only concern is whether using up one of the handful of as-yet-unused ASCII symbols raises the bar a little too high...
This is somewhat similar to the implicit lambda proposal in
http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Would it be worth extending this to multiple arguments? I think it would
be handy, but I can't think of any way to make it look nearly as good as the 0- and 1-arg forms.
Obviously this should still be a one-arg lambda that just uses its
parameter repeatedly:
: ? * x**2 + ? * x + ?
Sticking a number on the ? looks terrible:
: ?1 * x**2 + ?2 * x + ?3
Looking at the other DB-API 2.0 param styles, this is hideous because of
the way it uses colons twice:
: :1 * x**2 + :2 * x + :3
Something like this wouldn't be too bad, except that it already has a
much more useful meaning, set literals:
: {1} * x**2 + {2} * x + {3}
So really, the best I can come up with is the boost::lambda version I
already didn't like and you already dismissed without comment:
: _1 * x**2 + _2 * x + _3
On the other hand, any expression this complex--even with just one
parameter--is already starting to look pretty bad as an implicit lambda, and the downsides of an explicit lambda go down the longer it gets. So maybe the best answer is:
lambda a, b, c: a * x**2 + b * x + c
Anyway, we can always start with just 0- and 1-argument forms and come
back to expand the idea later, right?
However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343).
Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define.
I'll start digging up some examples; it is hard to predict how nice this
will look until we see it in a wide range of realistic code.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
+1 for the question mark. However, there's the problem of multi-argument lambdas. The ?(number) looks to much like Perl. What about something like ? being an array of arguments?
Doesn't that just make it even _more_ like perl, where arguments are always passed as an array?
Anyway, presumably you mean that this:
lambda *args: args[0] * x**2 + args[1] * x + args[2]
... could be written as:
: ?[0] * x**2 + ?[1] * x + ?[2]
I think that's horribly unreadable--partly because of the use of *args in the first place, and partly because ?[0] looks too... perlesque.
I think the best answer is the one I suggested above--and which I think Nick implicitly assumed because he's smart enough to get there directly: At least initially, just do 0-argument and 1-argument lambdas. As long as you leave the door open for extended syntaxes in a future proposal, we don't need to figure them out yet.
The question mark still looks like Perl. What about a reserved word instead? -- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
From: Ryan Gonzalez <rymg19@gmail.com> Sent: Friday, February 21, 2014 2:09 PM
On Fri, Feb 21, 2014 at 4:02 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Ryan Gonzalez <rymg19@gmail.com>
Sent: Friday, February 21, 2014 1:36 PM
On Fri, Feb 21, 2014 at 2:34 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
From: Nick Coghlan <ncoghlan@gmail.com>
Sent: Friday, February 21, 2014 4:18 AM
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html, I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers
That's really not a shorthand for lambda, it's a combination of other features that make lambdas often unnecessary. I think the C++ boost::lambda example is a better fit:
_1 ** 2 < n
but what if you
could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
I like this. The ? is a lot better than the _ or _1 I suggested (especially to people who do a lot of SQL programming in Python and already use ? as an inline parameter), and using a colon makes it explicit without adding too much visual noise. The only concern is whether using up one of the handful of as-yet-unused ASCII symbols raises the bar a little too high…
This is somewhat similar to the implicit lambda proposal in
http://legacy.python.org/dev/peps/pep-0312/, but with the following two essential differences:
1. The parentheses would be required (as per generator expressions, and as is being discussed for except expressions) 2. By using a "?" token within the implicit lambda, you would create a lambda that takes a single argument. If there is no such token, then it would take no arguments.
Would it be worth extending this to multiple arguments? I think it would be handy, but I can't think of any way to make it look nearly as good as the 0- and 1-arg forms.
Obviously this should still be a one-arg lambda that just uses its parameter repeatedly:
: ? * x**2 + ? * x + ?
Sticking a number on the ? looks terrible:
: ?1 * x**2 + ?2 * x + ?3
Looking at the other DB-API 2.0 param styles, this is hideous because of the way it uses colons twice:
: :1 * x**2 + :2 * x + :3
Something like this wouldn't be too bad, except that it already has a much more useful meaning, set literals:
: {1} * x**2 + {2} * x + {3}
So really, the best I can come up with is the boost::lambda version I already didn't like and you already dismissed without comment:
: _1 * x**2 + _2 * x + _3
On the other hand, any expression this complex—even with just one parameter—is already starting to look pretty bad as an implicit lambda, and the downsides of an explicit lambda go down the longer it gets. So maybe the best answer is:
lambda a, b, c: a * x**2 + b * x + c
Anyway, we can always start with just 0- and 1-argument forms and come back to expand the idea later, right?
However, the examples in that PEP are no longer useful, as they have since been addressed by dedicated constructs (conditional expressions in PEP 308 and context managers in PEP 343).
Note that I'm not saying this is a good idea. However, I am saying I think it is an idea that may be worth exploring further to see if it also addresses at least some of the use cases given in PEP's 403 and 3150 (especially in combination with PEP 463) by making custom key and predicate functions easier to define.
I'll start digging up some examples; it is hard to predict how nice this will look until we see it in a wide range of realistic code. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
+1 for the question mark. However, there's the problem of multi-argument lambdas. The ?(number) looks to much like Perl. What about something like ? being an array of arguments?
Doesn't that just make it even _more_ like perl, where arguments are always passed as an array?
Anyway, presumably you mean that this:
lambda *args: args[0] * x**2 + args[1] * x + args[2]
… could be written as:
: ?[0] * x**2 + ?[1] * x + ?[2]
I think that's horribly unreadable—partly because of the use of *args in the first place, and partly because ?[0] looks too… perlesque.
I think the best answer is the one I suggested above—and which I think Nick implicitly assumed because he's smart enough to get there directly: At least initially, just do 0-argument and 1-argument lambdas. As long as you leave the door open for extended syntaxes in a future proposal, we don't need to figure them out yet.
The question mark still looks like Perl.
What about a reserved word instead?
I can't think of any existing keyword that (a) is unambiguous in that syntax, or (b) doesn't read like someone with Wernicke's aphasia substituting random words. But I suppose a _new_ keyword could work: : arg **2 < n And that seems like it could be extended to multiple arguments pretty nicely by adding a "keyword schema", or just a parse rule that matches "arg" digit * instead of "arg": : arg1 * x**2 + arg2 * x + arg3 However, I'm pretty sure "arg" is used in lots of real-life code, especially in this context: if __name__ == '__main__': import sys for arg in sys.argv[1:]: print(spam(arg)) Any suggestions for a better keyword?
Guys, I had to page through *eight pages* of quotes, at least six levels deep, before I found a two-line new comment. Please [snip] unnecessarily quoted text. Leave enough to establish context, every email doesn't need to be a full archive of the entire discussion to this point. On Fri, Feb 21, 2014 at 04:09:47PM -0600, Ryan Gonzalez wrote: [...]
The question mark still looks like Perl.
What about a reserved word instead?
We already have perfectly good syntax for lambdas. Making them even more concise is not important enough to use another keyword, with the consequences of preventing code from using that word in the future, and breaking existing code that uses that word. I like lambda. But trying to make them even shorter and more concise (or if you want to be cynical, more cryptic) is not on my agenda. -1 -- Steven
Steven D'Aprano writes:
Guys, I had to page through *eight pages* of quotes, at least six levels deep, before I found a two-line new comment.
Indeed, if you're going to top-post, please do it at the *top*.
On 22 Feb 2014 14:51, "Stephen J. Turnbull" <stephen@xemacs.org> wrote:
Steven D'Aprano writes:
Guys, I had to page through *eight pages* of quotes, at least six
levels
deep, before I found a two-line new comment.
Indeed, if you're going to top-post, please do it at the *top*.
FWIW, when I do it, it's mostly due to a combination of the Gmail UI hiding quoted text by default and the phone interface making deleting large blocks of text annoying. Agreed it needs to be kept under control, but it's unfortunately easy to slip up and overquote given the default behaviour of at least Gmail and likely other mobile clients :P Cheers, Nick.
On Sat, Feb 22, 2014 at 8:24 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 22 Feb 2014 14:51, "Stephen J. Turnbull" <stephen@xemacs.org> wrote:
Steven D'Aprano writes:
Guys, I had to page through *eight pages* of quotes, at least six
levels
deep, before I found a two-line new comment.
Indeed, if you're going to top-post, please do it at the *top*.
FWIW, when I do it, it's mostly due to a combination of the Gmail UI hiding quoted text by default and the phone interface making deleting large blocks of text annoying.
Agreed it needs to be kept under control, but it's unfortunately easy to slip up and overquote given the default behaviour of at least Gmail and likely other mobile clients :P
Cheers, Nick.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Same thing here. I've been trying to bottom-post ever since someone here requested I do so, but it takes practice. -- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
On 22 Feb 2014 08:02, "Andrew Barnert" <abarnert@yahoo.com> wrote:
I think the best answer is the one I suggested above--and which I think
Nick implicitly assumed because he's smart enough to get there directly: At least initially, just do 0-argument and 1-argument lambdas. As long as you leave the door open for extended syntaxes in a future proposal, we don't need to figure them out yet. Yep, the implicit assumption was that the shorthand syntax could get away with handling just zero-argument functions (for simple callbacks and lazy evaluation) and one-positional-argument functions (for single object callbacks, sorting keys and filtering predicates). More complex cases don't need to be handled, as users have the option to revert to using an explicit lambda or a named function. This helps the proposal achieve one of useful heuristics to apply when judging a new piece of syntactic sugar: in the cases where it applies, it should be clearly superior to the existing alternatives. Once you get to 2+ arguments, it's hard to be clear without giving them names. Cheers, Nick.
From: Nick Coghlan <ncoghlan@gmail.com> Sent: Friday, February 21, 2014 3:28 PM
Yep, the implicit assumption was that the shorthand syntax could get away with handling just zero-argument functions (for simple callbacks and lazy evaluation) and one-positional-argument functions (for single object callbacks, sorting keys and filtering predicates).
More complex cases don't need to be handled, as users have the option to revert to using an explicit lambda or a named function. This helps the proposal achieve one of useful heuristics to apply when judging a new piece of syntactic sugar: in the cases where it applies, it should be clearly superior to the existing alternatives. Once you get to 2+ arguments, it's hard to be clear without giving them names.
After more thought on this, I realized that this really does solve all of the issues with the idea. Sure, it looks ugly when trying to define a function as a value in a dict (which is a common thing to do)—so don't use it there. In some contexts the ? looks wrong, so don't use it there either. And so on. As long as there are plenty of use cases where it _would_ make code more readable—like, say, creating the callbacks in a long chain of Tkinter widget constructors—it's a win. And, although it doesn't _actually_ solve the problem of removing the need for an extra abstraction, in many cases it _practically_ does. If you're reading those 20 widget constructors, yes, you do need to read and process the fact that a function is being created out of your expression on the first one—but on the next 19, your eye will just glide over it and you can read the expressions directly. Which solves the part of the problem that's worth solving. And I'm not sure it's possible to _actually_ solve the problem anyway, without Python becoming a very different language. So I ended up writing the threatened blog post anyway: http://stupidpythonideas.blogspot.com/2014/02/fixing-lambda.html So, I think it is worth reviving and extending PEP 312. What would help most in getting that idea considered? Gathering more concrete examples? Buiilding an implementation? Both?
On Mon, Feb 24, 2014 at 7:21 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
So, I think it is worth reviving and extending PEP 312. What would help most in getting that idea considered? Gathering more concrete examples? Buiilding an implementation? Both?
Examples are good. Especially if you can write a script that finds potentials. Feel free to use this script of mine as a basis: https://github.com/Rosuav/ExceptExpr/blob/master/find_except_expr.py ChrisA
On 24 Feb 2014 18:52, "Chris Angelico" <rosuav@gmail.com> wrote:
On Mon, Feb 24, 2014 at 7:21 PM, Andrew Barnert <abarnert@yahoo.com>
wrote:
So, I think it is worth reviving and extending PEP 312. What would help most in getting that idea considered? Gathering more concrete examples? Buiilding an implementation? Both?
Examples are good. Especially if you can write a script that finds potentials. Feel free to use this script of mine as a basis:
https://github.com/Rosuav/ExceptExpr/blob/master/find_except_expr.py
Yep, real world examples have always been a challenge in these discussions. Just looking for existing lambda expressions in the stdlib might be a good starting point. Cheers, Nick.
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Nick Coghlan wrote:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
-1, this is far too cryptic and ugly to have a place in Python. I'd be +0 on adding a while clause to for-loops, in *both* comprehension and statement forms, so that the above could be written isprime = all(n % p for p in primes_seen while p ** 2 < n) Adding a corresponding clause to the for-statement would allow the semantic definition of list comprehensions in terms of the statement expansion to be preserved. However, this all hinges on whether there is enough use for the feature to be worth adding. I'm yet to be convinced of that. -- Greg
First, given how often this comes up: Is it worth reopening PEP 3142 to at least add the additional considerations raised over the past 5 years, and maybe have Nick or someone write a better rejection message than a link to Guido's "I didn't know there was a PEP for that. I hereby reject it."? From: Greg Ewing <greg.ewing@canterbury.ac.nz> Sent: Friday, February 21, 2014 2:33 PM
Nick Coghlan wrote:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
-1, this is far too cryptic and ugly to have a place in Python.
I'd be +0 on adding a while clause to for-loops, in *both* comprehension and statement forms, so that the above could be written
isprime = all(n % p for p in primes_seen while p ** 2 < n)
Adding a corresponding clause to the for-statement would allow the semantic definition of list comprehensions in terms of the statement expansion to be preserved.
As I mentioned somewhere else, all of the languages that have the equivalent of while in comprehensions do it this way: the while is not a comprehension clause in its own, following the usual nesting rules like for and if, it's a modifier to the for clause. (In those languages, that's _also_ how if in comprehensions works, but there's no reason Python couldn't have a hybrid design, where while works on the Clojure/Racket model and if on the Miranda/Haskell model.) So: (n % p for p in primes_seen while p ** 2 < n if p != 2) … does not map to the nonsensical: for p in primes_seen: while p ** 2 < n: if p != 2: yield n % p … but to the sensible (and now legal, under the proposal): for p in primes_seen while p ** 2 < n: if p != 2: yield n % p
However, this all hinges on whether there is enough
use for the feature to be worth adding. I'm yet to be convinced of that.
I think an off-hand comment Nick made the last time around is what convinced me we probably don't need this (and inspired the start of my blog post). Just as an if clause is a syntactic way of writing filter, a while clause is a syntactic way of writing takewhile. If takewhile isn't even common enough to move from itertools to builtins, is it really common enough to move from a function to special syntax?
On 2014-02-21 12:18, Nick Coghlan wrote:
On 21 February 2014 20:24, Steven D'Aprano <steve@pearwood.info> wrote:
but alas both Nick Coglan and (if I recall correctly) Guido have ruled that Python won't get this, so until the Revolution comes, it isn't going to happen.
It's not that it can't happen - it's that someone would need to build a case of a similar calibre to the one Chris Angelico is currently putting together for except expressions in PEP 463 :)
However, it's a *much* bigger challenge in this case, as itertools.takewhile exists, whereas there's currently no way to do exception handling as part of a larger expression without major contortions.
Re-reading Andrew's post at http://stupidpythonideas.blogspot.com.au/2013/07/syntactic-takewhile.html, I'm actually more intrigued by Haskell's shorthand for lambda functions that consist of a single binary operator.
Consider:
isprime = all(n % p for p in takewhile((lambda p: p**2 < n), primes_seen))
Is there are a nicer way to write that? The Haskell equivalent is:
(< n) . (** 2)
That's not very readable to most Python programmers, but what if you could write something like:
isprime = all(n % p for p in takewhile((: ? ** 2 < n), primes_seen))
[snip] C# has a lambda operator "=>". That would give: isprime = all(n % p for p in takewhile((x => x ** 2 < n), primes_seen))
participants (16)
-
Andrew Barnert
-
Antoine Pitrou
-
Carl Smith
-
Chris Angelico
-
David Mertz
-
Greg Ewing
-
M.-A. Lemburg
-
Masklinn
-
Mathias Panzenböck
-
MRAB
-
Nick Coghlan
-
Oscar Benjamin
-
Ryan Gonzalez
-
Stephen J. Turnbull
-
Steven D'Aprano
-
אלעזר