Re: [Python-ideas] Special keyword denoting an infinite loop
On 29 June 2014 00:56, Benny Khoo <benny_khoo_99@yahoo.com> wrote:
rather than a special keyword in Python, how about having Python to support the concept of passing block (a group of statements) as argument? I thought that can be quite elegant solution. So a loop statement can be interpreted simply as a function that accept a block e.g. loop [block]?
Supporting block has a lot of practical applications. I remember seeing some special purpose flow control functions as early as Tcl. We also see it in Ruby and the more recently the new Swift language.
This is a well worn path, and it's difficult to retrofit to an existing language. Ruby, at least, relies heavily on a convention of taking blocks as the last argument to a function to make things work, which is a poor fit to Python's keyword arguments and far more varied positional signatures for higher order functions. PEP 403 and PEP 3150 are a couple of different explorations of the idea a more block-like feature. http://python-notes.curiousefficiency.org/en/latest/pep_ideas/suite_expr.htm... is one that goes even further to consider a delineated subsyntax for Python that would allow entire suites as expressions. However, the stumbling block all these proposals tend to hit is that proponents really, really, struggle to come up with compelling use cases where "just define a named function" isn't a clearer and easier to understand answer. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Jun 28, 2014, at 8:14, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 29 June 2014 00:56, Benny Khoo <benny_khoo_99@yahoo.com> wrote:
rather than a special keyword in Python, how about having Python to support the concept of passing block (a group of statements) as argument? I thought that can be quite elegant solution. So a loop statement can be interpreted simply as a function that accept a block e.g. loop [block]?
Supporting block has a lot of practical applications. I remember seeing some special purpose flow control functions as early as Tcl. We also see it in Ruby and the more recently the new Swift language.
This is a well worn path, and it's difficult to retrofit to an existing language. Ruby, at least, relies heavily on a convention of taking blocks as the last argument to a function to make things work, which is a poor fit to Python's keyword arguments and far more varied positional signatures for higher order functions.
Since Benny mentioned Swift, it's probably worth following up on that. Swift doesn't actually have blocks* (somewhat surprising, since Apple previously added blocks to ObjC and even C); it has a clever way of getting all the benefits of blocks without the downsides. Could there be something for Python there? In Ruby (and ObjC) blocks and functions are different types of things. They're defined, called, and passed differently; they have different scope semantics (functions can't capture local variables, blocks can); they can't even easily be converted to each other. Like Python, Swift functions are closures. Swift functions can be defined in two ways, but either way, they're the same kind of function. Just like Python's def and lambda. Their func statement is almost exactly like our def statement except with braces. Their inline closure expression is similar to our lambda expression, but with some major differences (most of which have actually been proposed for Python): no keyword to introduce the expression, params go inside the braces, params can be anonymous (so { $1 + $2 } is a complete definition, equivalent to lambda _1, _2: _1 + _2), and of course they can be multiline and contain statements. (And like ours, return isn't necessary.) Then Swift added one tiny pieces of syntactic sugar: if an anonymous function definition is the last argument in a function call, it can go outside the parens. So, you can write this: reduce(myArray, 0) { $1 + $2 } myArray.filter { $1 >= 0 } That looks just like Ruby blocks, but it's still just functions. So if you already have a function defined out of line (or a bound method, or a function you received from elsewhere and stored in a variable, or whatever), you don't need to wrap it in a block, you just pass it: reduce(myArray, "", smartConcat) myArray.filter(myPredicate) And that looks just like Python or Lisp. And if you want to write a function that takes two functions, with an optional keyword argument after them, it can still take them both inline, quite readable. Ruby users claim they don't miss this ability, but anyone who uses promises in JS (or anything at all in Haskell) can think of dozens of times they passed non-final function arguments today, and wouldn't be happy with an API that made that impossible. So, if we adopted Nick's not-really-serious proposals for omitting lambda before the colon when it's not syntactically ambiguous and allowing anonymous _1-style params, and added the syntactic sugar to allow lambdas as final arguments to come outside the parens, would that make Python better? reduce(my_list, 0, lambda x, y: x + y) reduce(my_list, 0) :_1 + _2 I think the general agreement on the first two changes was that, while they can be nice in a few cases, they can also be very ugly--and, more importantly, simple cases are already good enough today (see below), while more complex cases can't be done without multiline lambdas so there's no help. And I don't think the last bit of sugar sways things. If we had a way to do multiline (statement-having) lambdas, that might be another story. But after years of trying, nobody's come up with a good solution, so you can't just assume that's solvable. Solve that first, and then we should definitely look at how the Swift stuff could be added on top of that. Meanwhile, I think Nick's PEP 403, or something like it, is both more general and more Pythonic. Instead of trying to find a better way to embed function definitions into expressions, find a way to lift any subexpression out of an expression and define it (normally) after the current statement, and you solve the current problem for free, and a bunch of other problems too. (Although, unfortunately, none that really seem to demand solutions in practical code.) --- * I lied a little at the top. Because Swift compiles into code that interacts with the ObjC runtime and Apple's blocks-filled C frameworks, of course it has to deal with ObjC blocks, in both directions. But it does this the same way it deals with C functions--by transparently bridging them to plain-old Swift functions. Unless you want to dig into the bridging using as-yet-undocumented stdlib functions, you never see blocks anywhere.
On 29 June 2014 07:33, Andrew Barnert <abarnert@yahoo.com> wrote:
Meanwhile, I think Nick's PEP 403, or something like it, is both more general and more Pythonic. Instead of trying to find a better way to embed function definitions into expressions, find a way to lift any subexpression out of an expression and define it (normally) after the current statement, and you solve the current problem for free, and a bunch of other problems too. (Although, unfortunately, none that really seem to demand solutions in practical code.)
On that last point, one of my goals at SciPy next month will be to encourage folks in the scientific community that are keen to see something resembling block support in Python to go hunting for compelling *use cases*. The fatal barrier to proposals like PEP 403 and 3150 has long been that there are other options already available, so the substantial additional complexity they introduce isn't adequately justified. The two main stumbling blocks: - generators-as-coroutines already offer a way of suspending execution of a sequential operation, as embodied in asyncio.coroutine and contexlib.contextmanager - nested definitions of named functions are usually a readable alternative in the cases lambdas can't handle The reason I occasionally spend time on PEPs 403 and 3150 is because I think we're missing a case where "one shot" functions could be handled more gracefully - situations where we're defining a function solely because we want to pass it to other code as an object at runtime, not because we need to reference it at multiple places in the *source* code. That's a pretty narrow niche, though - if you *do* need to invoke the same code in multiple places, than a named function is always going to be better, even if dedicated one-shot function support is available. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Hi On Sun, Jun 29, 2014 at 2:19 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On that last point, one of my goals at SciPy next month will be to encourage folks in the scientific community that are keen to see something resembling block support in Python to go hunting for compelling *use cases*. The fatal barrier to proposals like PEP 403 and 3150 has long been that there are other options already available, so the substantial additional complexity they introduce isn't adequately justified. The two main stumbling blocks:
What is the status of PEP 3150? I remember reading that you were withdrawing 3150 in favor of 403 but this is not reflected in http://legacy.python.org/dev/peps/pep-3150/. cheers, Hernán
On 29 Jun 2014 23:00, "Hernan Grecco" <hernan.grecco@gmail.com> wrote:
Hi
On Sun, Jun 29, 2014 at 2:19 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On that last point, one of my goals at SciPy next month will be to encourage folks in the scientific community that are keen to see something resembling block support in Python to go hunting for compelling *use cases*. The fatal barrier to proposals like PEP 403 and 3150 has long been that there are other options already available, so the substantial additional complexity they introduce isn't adequately justified. The two main stumbling blocks:
What is the status of PEP 3150? I remember reading that you were withdrawing 3150 in favor of 403 but this is not reflected in http://legacy.python.org/dev/peps/pep-3150/.
I see merit in both alternatives, so I still update both of them occasionally. I did withdraw 3150 at one point, but I later figured out a possible solution to the previously fatal flaw in its namespace handling semantics and moved it back to Deferred. I tend not to announce any updates to either of them, since they'll remain pure speculation in the absence of clear use cases where they would provide a compelling readability benefit over the status quo. Cheers, Nick.
cheers,
Hernán _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 29.06.2014 15:37, Nick Coghlan wrote:
On 29 Jun 2014 23:00, "Hernan Grecco" <hernan.grecco@gmail.com> wrote:
Hi
On Sun, Jun 29, 2014 at 2:19 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On that last point, one of my goals at SciPy next month will be to encourage folks in the scientific community that are keen to see something resembling block support in Python to go hunting for compelling *use cases*. The fatal barrier to proposals like PEP 403 and 3150 has long been that there are other options already available, so the substantial additional complexity they introduce isn't adequately justified. The two main stumbling blocks:
What is the status of PEP 3150? I remember reading that you were withdrawing 3150 in favor of 403 but this is not reflected in http://legacy.python.org/dev/peps/pep-3150/.
I see merit in both alternatives, so I still update both of them occasionally. I did withdraw 3150 at one point, but I later figured out a possible solution to the previously fatal flaw in its namespace handling semantics and moved it back to Deferred.
It is still written in the abstract of 403 that 3150 was withdrawn. regards, jwi p.s.: while I’m at it, in the “Explaining Decorator Clause Evaluation and Application”, 3150 is missing a ?, I think, and in the “Out of Order Execution” section there seems to be a markup issue after the second code block
I tend not to announce any updates to either of them, since they'll remain pure speculation in the absence of clear use cases where they would provide a compelling readability benefit over the status quo.
Cheers, Nick.
cheers,
Hernán _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Sorry, just realized I left out the example I meant to give. I'll insert it below: On Saturday, June 28, 2014 2:37 PM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: [snip]
And if you want to write a function that takes two functions, with an optional keyword argument after them, it can still take them both inline, quite readable. Ruby users claim they don't miss this ability, but anyone who uses promises in JS (or anything at all in Haskell) can think of dozens of times they passed non-final function arguments today, and wouldn't be happy with an API that made that impossible.
Here's some slightly simplified real-life JS code using Promises: db.select_one(sql, thingid) .then(function(rowset) { return rowset[0]['foo']; }, log_error); Here's what the same code looks like with a Ruby port of Promises: db.select_one(sql, thingid) .then {|rowset| rowset[0]['foo'} .then(nil, proc {|err| log_error(err)}) I think this shows why blocks are a second-rate substitute for first-class, closure-capturing, inline-definable functions (which Ruby and ObjC don't have, but JS and Swift do). The only reason anyone should want blocks in Python is if they're convinced that it's impossible to come up with a clean syntax for multiline lambdas, but it's easy to come up with one for multiline blocks.
participants (4)
-
Andrew Barnert
-
Hernan Grecco
-
Jonas Wielicki
-
Nick Coghlan