Enhance definition of functions
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all. In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}" Python (proposed): def func(a,b): print(a+b) return a+b becomes func=function a,b: print(a+b) return a+b
What's wrong with the current syntax? Why can't you just write a def? I didn't do an exact count, but it looks like the lengths of the two function definitions differ by at most a single character. On Tue, Jul 30, 2013 at 11:19 AM, Musical Notation < musicdenotation@gmail.com> wrote:
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all. In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}" Python (proposed): def func(a,b): print(a+b) return a+b
becomes
func=function a,b: print(a+b) return a+b
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On Tue, Jul 30, 2013 at 11:19 AM, Musical Notation
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all. In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}"
This seems like an odd misunderstanding to me. Of course Javascript has a lambda, it just happens to spell it 'f-u-n-c-t-i-o-n' rather than like 'l-a-m-b-d-a'. As with every other object, a lambda object can be assigned a name in Javascript... although it need not be, of course. You can also just use a Javascript lambda inline anywhere a code object makes sense, e.g.: higher_order_func(function(x,y){return x+y}, 2, 3); Of course, if you wanted to, you could have written: add2 = function(x,y){return x+y}; higher_order_func(add2, 2, 3); Or likewise: function add2(x,y){return x+y}; higher_order_func(add2, 2, 3); Other than not allowing full blocks in lambdas, Python is exactly the same. higher_order_func(lambda x,y: x+y, 2, 3) And as with Javascript, you *could* give the passed function a name with: add2 = lambda x,y: x+y Or with: def add2(x,y): return x+y It sounds like what you are asking for--after saying Python will never have it--is multi-line, full-block, lambdas in Python. For that, it has been discussed a lot of times, and no one has found a syntax that feel widely acceptable. -- 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 30 Jul, 2013, at 17:19, Musical Notation
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all.
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}"
That's just a lambda by another name.
Python (proposed): def func(a,b): print(a+b) return a+b
becomes
func=function a,b: print(a+b) return a+b
This has a number a problems, as noted by Matthew this isn't shorter than the corresponding named function. It also introduces a new keyword, and because of that likely breaks existing code (especially because "function" is a fairly common word in IT). The syntax appears to indicate that the new construct is an expression, if so how would you use it in other contexts where expressions can be used(Such as function arguments, list literals, ...)? Ronald
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>'
is inferior to a possibly unique and meaningful name. This is not just
in tracebacks. Consider
[<built-in function sin>, <built-in function cos>]
versus
[
On 31/07/13 11:41, Terry Reedy wrote:
On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ]
True, but if we're going to hypothesize nice syntax for multi-line lambdas, it's not much harder to imagine that there's also nice syntax to give them a name and a doc string at the same time :-) I-don't-want-much-just-multi-line-lambda-and-ultimate-power-ly yr's, -- Steven (and I don't care that much about multi-line lambda)
On 7/31/2013 1:20 AM, Steven D'Aprano wrote:
On 31/07/13 11:41, Terry Reedy wrote:
On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ] True, but if we're going to hypothesize nice syntax for multi-line lambdas, it's not much harder to imagine that there's also nice syntax to give them a name and a doc string at the same time :-)
But then they would not be anonymous. When I have a multiple line expression, I often pull out an anonymous expression and give it a local name to use within the expression to make the expression shorter and more comprehensible. So does most everyone else. So I have no desire to do the opposite with function definitions by sticking multiple line definition in the middle of an expression. I know Lisper do that, and the result is ofter difficult to impossible to read. In sort(key=?) calls, I use lambda for short, one-use key expressions, but I would never want to inject a multiple line expression into the middle. To me, it make the code less readable than separating a non-trivial key idea from the sort idea. -- Terry Jan Reedy
On 07/30/2013 10:20 PM, Steven D'Aprano wrote:
On 31/07/13 11:41, Terry Reedy wrote:
On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ] True, but if we're going to hypothesize nice syntax for multi-line lambdas, it's not much harder to imagine that there's also nice syntax to give them a name and a doc string at the same time :-)
We already have nice syntax to assign a name and doc string at the same time -- it's called `def`. ;) -- ~Ethan~
On 31 Jul, 2013, at 3:41, Terry Reedy
On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ]
It might be lack of imagination on my part, but I have a lot of nested functions named "function" or "callback" that are too complex to be a lambda, but too simple or specialized to bother making them proper functions. The key function for sort is one of the usecases. I'd love to have anonymous functions for that, but haven't seen a proposal for those yet that would fit the language. Ronald
I'd like multiline lambdas too, but the syntax is a thorny problem. F# is
the only language I know (are there others?) that allows you to mix
whitespace-delimited and paren-delimited expressions, e.g. with
whitespace-blocks inside parens:
let f n = n + (
if n % 2 = 0 then
printf "lol"
1
else
printf "omg"
2
)
f 0
// lol
// 1
f 1
// omg
// 3
f 2
// lol
// 3
And it actually works as you'd expect, most of the time, so clearly doing
such a thing is possible.
On the other hand, even with lots of smart people behind F#, edge cases in
the parsing of this sort of thing bites me pretty regularly. Although It'd
be awesome if it could be implemented cleanly and predictably, I definitely
do not want to be the one writing the grammar or parser to support this
kind of syntax!
-Haoyi
On Wed, Jul 31, 2013 at 2:23 PM, Ronald Oussoren
On 31 Jul, 2013, at 3:41, Terry Reedy
wrote: On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ] It might be lack of imagination on my part, but I have a lot of nested functions named "function" or "callback" that are too complex to be a lambda, but too simple or specialized to bother making them proper functions. The key function for sort is one of the usecases.
I'd love to have anonymous functions for that, but haven't seen a proposal for those yet that would fit the language.
Ronald _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 2013-07-31, at 08:37 , Haoyi Li wrote:
I'd like multiline lambdas too, but the syntax is a thorny problem. F# is the only language I know (are there others?) that allows you to mix whitespace-delimited and paren-delimited expressions, e.g. with whitespace-blocks inside parens:
let f n = n + ( if n % 2 = 0 then printf "lol" 1 else printf "omg" 2 )
Haskell can do that: f n = n + ( case n `mod` 2 of 0 -> unsafePerformIO $ do putStrLn "lol" return 1 1 -> unsafePerformIO $ do putStrLn "omfg" return 2 ) although a difference is that it doesn't have statement blocks, only expressions (`do` blocks are sugar for monadic chain expressions)
On Jul 30, 2013, at 23:23, Ronald Oussoren
On 31 Jul, 2013, at 3:41, Terry Reedy
wrote: On 7/30/2013 11:59 AM, Ronald Oussoren wrote:
"Never" is a long time. AFAIK the main reason why Python doesn't have multi-line lambda's is that nobody has proposed a suitable syntax yet (and not for lack of trying, the archives of this list and python-dev contain a lot of proposals that were found lacking).
There is also the fact that a generic .__name__ attribute of '<lambda>' is inferior to a possibly unique and meaningful name. This is not just in tracebacks. Consider [<built-in function sin>, <built-in function cos>] versus [
, ] It might be lack of imagination on my part, but I have a lot of nested functions named "function" or "callback" that are too complex to be a lambda, but too simple or specialized to bother making them proper functions. The key function for sort is one of the usecases.
I'd love to have anonymous functions for that, but haven't seen a proposal for those yet that would fit the language.
Would it really help anything? If you're worried about keystrokes you can always call them "f" instead of "function". And I don't think anonymous functions would be as nice in tracebacks as even genetically-named ones. I think having to define them out of line is usually a more serious problem than having to name them, and if you solve that problem you may get the other one for free (although admittedly you may not, as the @in proposal shows...). Of course often, when I run into this, it's a matter of trying to write something that _could_ be an expression, without even needing a lambda... except that it would be a huge mess of partial and compose and other HOF calls and uses of operator functions and sometimes itertools stuff that would all be trivial in a different language but is horribly unpleasant in Python. And then I can compare the cost of having to write an out of line function in Python to the cost of writing the surrounding code in Haskell and I feel better. :)
On 31 July 2013 07:47, Andrew Barnert
It might be lack of imagination on my part, but I have a lot of nested functions named "function" or "callback" that are too complex to be a lambda, but too simple or specialized to bother making them proper functions. The key function for sort is one of the usecases.
I'd love to have anonymous functions for that, but haven't seen a proposal for those yet that would fit the language.
Would it really help anything? If you're worried about keystrokes you can always call them "f" instead of "function". And I don't think anonymous functions would be as nice in tracebacks as even genetically-named ones.
I think having to define them out of line is usually a more serious problem than having to name them, and if you solve that problem you may get the other one for free (although admittedly you may not, as the @in proposal shows...).
The only real reason I ever use lambdas (and would sometimes like a multiline version or similar) is for readability, where I want to pass a callback to a function and naming it and placing it before the call over-emphasises its importance. It's hard to make this objective, but to my eyes def k(obj): return obj['x'] / obj['y'] s = list(sorted(l, key=k) reads marginally worse than s = list(sorted(l, key=k)) where: def k(obj): return obj['x'] / obj['y'] simply because the focus of the block of code (building a sorted list) is at the start in the latter. But because the difference is so subtle, it's very hard to get a syntax that improves things sufficiently to justify new syntax. And it's also not at all obvious to me that any improvement in readability that can be gained in simple example code that you can post in an email, will actually still be present in "real world" code (which, in my experience, is always far messier than constructed examples :-)) Paul
From: Paul Moore
On 31 July 2013 07:47, Andrew Barnert
wrote: It might be lack of imagination on my part, but I have a lot of nested functions named "function" or "callback" that are too complex to be a lambda, but too simple or specialized to bother making them proper functions. The key function for sort is one of the usecases.
I'd love to have anonymous functions for that, but haven't seen a proposal for those yet that would fit the language.
Would it really help anything? If you're worried about keystrokes you can always call them "f" instead of "function". And I don't think anonymous functions would be as nice in tracebacks as even genetically-named ones.
I think having to define them out of line is usually a more serious problem than having to name them, and if you solve that problem you may get the other one for free (although admittedly you may not, as the @in proposal shows...).
The only real reason I ever use lambdas (and would sometimes like a multiline version or similar) is for readability, where I want to pass a callback to a function and naming it and placing it before the call over-emphasises its importance. It's hard to make this objective, but to my eyes
def k(obj): return obj['x'] / obj['y'] s = list(sorted(l, key=k)
reads marginally worse than
s = list(sorted(l, key=k)) where: def k(obj): return obj['x'] / obj['y']
simply because the focus of the block of code (building a sorted list) is at the start in the latter.
This is "the @in proposal" I referenced earlier—specifically, PEP 403 (http://www.python.org/dev/peps/pep-0403/). With PEP 403, your code would actually look like this: @in s = list(sorted(l, key=k)) def k(obj): return obj['x'] / obj['y'] Your syntax is closer to that of PEP 3150, but if you look toward the end of PEP 403, it specifically describes using PEP 3150-like syntax for the (simpler) PEP 403 semantics, and the result is exactly your suggestion except with the keyword "given" instead of "where".
But because the difference is so subtle, it's very hard to get a syntax that improves things sufficiently to justify new syntax. And it's also not at all obvious to me that any improvement in readability that can be gained in simple example code that you can post in an email, will actually still be present in "real world" code (which, in my experience, is always far messier than constructed examples :-))
The motivating examples in PEP 403 are, like yours, marginally better, for pretty much the same reason—putting the focus of the code at the start. And when PEP 403 pops up in relation to some different proposal, it's "if we had PEP 403, there would be less reason to want this new idea… but still not zero". And so on. It feels like there should be more benefit to the idea than this, but nobody's found it yet. And that's why it's stalled and deferred. If you can come up with a better motivating example, even if it's too hard to put into an email, that could definitely be helpful. But anyway, I think you're mostly agreeing with me. When neither lambda nor def feels right, it's usually not because you really want a multi-line expression, or a multi-line anonymous function, but because you want to get the petty details of the function "out of the way" of the important code, right?
On 1 August 2013 09:17, Andrew Barnert
But anyway, I think you're mostly agreeing with me. When neither lambda nor def feels right, it's usually not because you really want a multi-line expression, or a multi-line anonymous function, but because you want to get the petty details of the function "out of the way" of the important code, right?
Yeah, I've been grappling with this problem for years, which is why I have two competing deferred PEPs about it :) For certain kinds of problem, the natural way to think about them is as "I want to do X", and as an incidental part of doing X, you need to define a function that does Y. Sorting, complex comprehensions, various flavours of event driven programming (especially GUI programming, where the yield-driven approach of PEP 3156 may not be appropriate, as well as the low level transport code for PEP 3156 style systems). Lambda isn't a great solution because it embeds all the complexity of the function directly in the main expression, obscuring the fact that the overall operation is "do X". Ruby's block syntax is an elegant solution, but the specific problem with adapting that to Python is deciding how to spell the forward reference to the trailing definition. Ruby solves that problem through a convention (the block is just the last positional argument), but Python has no such convention - there's a wide variety of signatures for higher order operations, and a syntax that required new signatures for everything is no solution at all. PEP 403 is the approach I dislike least so far, but that's a far cry from being something I'm willing to propose for inclusion in the language. The "@in" does hint at the out of order execution nicely, but it's also a bit too heavy (drawing attention away from the subsequent simple statement), and as an ordinary name, the forward reference doesn't quite stand out enough. PEP 3150 could possibly by improved by having the hidden function implicitly end with (a more efficient equivalent of) "return types.SimpleNamespace(**locals())" and introducing "?" as a forward reference to that result: sorted_data = sorted(data, key=?.k) given: def k(item): return item.attr1, item.attr2 But it's hardly what one could call a *concise* syntax. On the other hand, it *does* let you do some pretty neat things, like: dispatch_table = vars(?) given: def command1(*args, **kwds): ... def command2(*args, **kwds): ... def command3(*args, **kwds): ... I would also tweak the early binding syntax to require an additional keyword to make it read more like English: seq = [] for i in range(10): seq.append(?.f) given i=i in: def f(): return i assert [f() for f in seq] == list(range(10)) Using up another precious symbol would be a big call, but it's starting to feel more like something of sufficient power to justify new syntax. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Le Fri, 2 Aug 2013 22:48:33 +1000,
Nick Coghlan
I would also tweak the early binding syntax to require an additional keyword to make it read more like English:
seq = [] for i in range(10): seq.append(?.f) given i=i in: def f(): return i assert [f() for f in seq] == list(range(10))
I think any "inline function" proposal should focus on callback-based programming for its use cases. In this context, you usually have one or two callbacks (two in Twisted-style programming: one for success, one for failure), passed positionally to a consuming function: loop.create_connection((host, port), @cb, @eb) where: def cb(sock): # Do something with socket def eb(exc): logging.exception( "Failed connecting to %s:%s", host, port) Regards Antoine.
On 2 August 2013 23:55, Antoine Pitrou
Le Fri, 2 Aug 2013 22:48:33 +1000, Nick Coghlan
a écrit : I would also tweak the early binding syntax to require an additional keyword to make it read more like English:
seq = [] for i in range(10): seq.append(?.f) given i=i in: def f(): return i assert [f() for f in seq] == list(range(10))
I think any "inline function" proposal should focus on callback-based programming for its use cases.
I think callback based programming is a *good* use case, certainly, but not the only one.
In this context, you usually have one or two callbacks (two in Twisted-style programming: one for success, one for failure), passed positionally to a consuming function:
loop.create_connection((host, port), @cb, @eb) where: def cb(sock): # Do something with socket def eb(exc): logging.exception( "Failed connecting to %s:%s", host, port)
We can't use 'where' because we know it conflicts with the SQL sense of the term in too many APIs. We're reasonably sure we can get away with "given" without too much conflict, though. Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause): @something() given: ... I liked the notion of "?" as suggesting doubt and uncertainty - an element of "leave this undefined for now, we'll fill it in later". While the out of order execution is related to decorators (hence @in for PEP 403), I think PEP 3150 is more of a different notion, especially with the revisions I suggested in this thread. I believe your example still looks reasonable with the "?." notation for the forward reference: loop.create_connection((host, port), ?.cb, ?.eb) given: def cb(sock): # Do something with socket def eb(exc): logging.exception( "Failed connecting to %s:%s", host, port) Anyway, not something that's going to happen for 3.4, but a problem I'm happy to keep chipping away at - some day we might find a proposed solution that doesn't send Guido screaming in the other direction :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Le Sat, 3 Aug 2013 01:46:37 +1000,
Nick Coghlan
In this context, you usually have one or two callbacks (two in Twisted-style programming: one for success, one for failure), passed positionally to a consuming function:
loop.create_connection((host, port), @cb, @eb) where: def cb(sock): # Do something with socket def eb(exc): logging.exception( "Failed connecting to %s:%s", host, port)
We can't use 'where' because we know it conflicts with the SQL sense of the term in too many APIs. We're reasonably sure we can get away with "given" without too much conflict, though.
How about reusing "with"? There's no ambiguity with context managers since the syntactic context is different.
Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
I liked the notion of "?" as suggesting doubt and uncertainty - an element of "leave this undefined for now, we'll fill it in later".
I don't really like it :-) "?" has other meanings traditionally: as part of the ternary operator in C-like languages (many of them), as a wildcard character in pattern matching languages, as a marker of optional matchers in regular expressions. Also, I really don't like the idea that "?" represents a full-blown object with attribute access capabilities and whatnot. It smells too much like Perl-style (Ruby-style?) magic variables. My proposal is more limited: it's a syntactic addition, but it doesn't create new runtime objects or types. Regards Antoine.
On Aug 2, 2013, at 9:00, Antoine Pitrou
Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
That's fine if callbacks are the _only_ case you want to handle, but as Nick just explained, there are many other cases that are also useful. The middle of an if expression in a comprehension, for example, isn't a function parameter. Also, when you have a long function call expression--as you almost always do in, say, PyObjC or PyWin32 GUIs--you often want to put each parameter on its own line. While that won't confuse the parser, it could easily confuse a human, who will see "@callback," on a line by itself and think "decorator". It's probably worth taking some real examples from a bunch of different domains where you've defined something out-of-line but would use this proposal if you could, and rewriting them with each variation to see what they look like.
On Fri, Aug 2, 2013 at 6:25 PM, Andrew Barnert
On Aug 2, 2013, at 9:00, Antoine Pitrou
wrote: Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
That's fine if callbacks are the _only_ case you want to handle, but as Nick just explained, there are many other cases that are also useful. The middle of an if expression in a comprehension, for example, isn't a function parameter.
Also, when you have a long function call expression--as you almost always do in, say, PyObjC or PyWin32 GUIs--you often want to put each parameter on its own line. While that won't confuse the parser, it could easily confuse a human, who will see "@callback," on a line by itself and think "decorator".
It's probably worth taking some real examples from a bunch of different domains where you've defined something out-of-line but would use this proposal if you could, and rewriting them with each variation to see what they look like.
Is the reason to mark up the variables that are defined in the with/where/given block only a technical one (to support the parser)? Best wishes, Michael
On 3 August 2013 02:41, Michael Walter
On Fri, Aug 2, 2013 at 6:25 PM, Andrew Barnert
wrote: On Aug 2, 2013, at 9:00, Antoine Pitrou
wrote: Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
That's fine if callbacks are the _only_ case you want to handle, but as Nick just explained, there are many other cases that are also useful. The middle of an if expression in a comprehension, for example, isn't a function parameter.
Also, when you have a long function call expression--as you almost always do in, say, PyObjC or PyWin32 GUIs--you often want to put each parameter on its own line. While that won't confuse the parser, it could easily confuse a human, who will see "@callback," on a line by itself and think "decorator".
It's probably worth taking some real examples from a bunch of different domains where you've defined something out-of-line but would use this proposal if you could, and rewriting them with each variation to see what they look like.
Is the reason to mark up the variables that are defined in the with/where/given block only a technical one (to support the parser)?
It's more than just the parser that needs that extra help to make it implementable :) However, the syntactic marker is helpful for human readers, too - it makes it clear which names come from the statement local namespace, and which are just ordinary name references. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, 2 Aug 2013 09:25:05 -0700
Andrew Barnert
On Aug 2, 2013, at 9:00, Antoine Pitrou
wrote: Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
That's fine if callbacks are the _only_ case you want to handle, but as Nick just explained, there are many other cases that are also useful. The middle of an if expression in a comprehension, for example, isn't a function parameter.
There may be many other cases, but almost any time people complain about the lack of inline functions, it's in situations where they are declaring callbacks (i.e. GUI- or network-programming). So, yes, I don't think the other use cases should be a primary point of concern.
Also, when you have a long function call expression--as you almost always do in, say, PyObjC or PyWin32 GUIs--you often want to put each parameter on its own line. While that won't confuse the parser, it could easily confuse a human, who will see "@callback," on a line by itself and think "decorator".
Even if indented, inside a parenthesis and not preceding a similarly indented "def"? Regards Antoine.
PEP 3150 is actually pretty simple to implement using macros:
@in(map(_, [1, 2, 3])) ... def thing(x): ... return x + 1 ... thing [2, 3, 4]
The macro is about 5 lines long, since it's exactly the same as my 15-line
quick lambda macro (i.e. f[_ + _] -> lambda a, b: a + b); this should have
worked out of the box, requiring no additional code:
@f[map(_, [1, 2, 3]]
def thing(x):
return x + 1
But it doesn't due to the parser not liking [] in decorators; boo for
arbitrary syntactic restrictions =( =( =(.
This only works for higher-order-functions with a single callback. As
things stand now, the status-quo solution for multi-callback functions is
to make a class and inherit from it, and fill in the methods you need to
fill in. There wasn't any PEP for this but that's what people are doing all
over the place: many many classes exist purely to let people fill in the
missing functions. Not because the class objects have any state, or you
ever intend to create objects which will live for more than one function
call:
class MyClass(BaseRequestClass):
def print_data(d):
print d
def print_error(failure):
sys.sys.stderr.write(str(failure))
result = MyClass().make_request()
# never gonna use MyClass() ever again!
Java uses this pattern too, and I do not like it. One possibility, though,
would just codify/streamline the status quo, via macros:
@in(make_request(_, _))
class result:
def print_data(d):
print d
def print_error(failure):
sys.sys.stderr.write(str(failure))
Which would desugar (using macro-magic and metaclasses) into
def print_data(d):
print d
def print_error(failure):
sys.sys.stderr.write(str(failure))
result = make_request(print_data, print_error)
Potential bikesheds are over whether to use `_` to leave holes, or
`print_data` and `print_error` as named arguments, as well as whether
`result` should be a class or function def. Overall, though, it looks
exactly like PEP3150 except this
public_name = ?.MeaningfulClassName(*params) given:
class MeaningfulClassName():
...
becomes this:
@in(_.MeaningfulClassName(*params))
class public_name:
class MeaningfulClassName():
...
Which is pretty close. I can put up the implementations of all of these
things if anyone's interested in playing around with the syntax/semantics
in the REPL.
-Haoyi
On Sat, Aug 3, 2013 at 1:46 AM, Antoine Pitrou
On Fri, 2 Aug 2013 09:25:05 -0700 Andrew Barnert
wrote: On Aug 2, 2013, at 9:00, Antoine Pitrou
wrote: Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
That's fine if callbacks are the _only_ case you want to handle, but as Nick just explained, there are many other cases that are also useful. The middle of an if expression in a comprehension, for example, isn't a function parameter.
There may be many other cases, but almost any time people complain about the lack of inline functions, it's in situations where they are declaring callbacks (i.e. GUI- or network-programming). So, yes, I don't think the other use cases should be a primary point of concern.
Also, when you have a long function call expression--as you almost always do in, say, PyObjC or PyWin32 GUIs--you often want to put each parameter on its own line. While that won't confuse the parser, it could easily confuse a human, who will see "@callback," on a line by itself and think "decorator".
Even if indented, inside a parenthesis and not preceding a similarly indented "def"?
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 08/02/2013 10:46 AM, Antoine Pitrou wrote:
There may be many other cases, but almost any time people complain about the lack of inline functions, it's in situations where they are declaring callbacks (i.e. GUI- or network-programming). So, yes, I don't think the other use cases should be a primary point of concern.
So long as the other use-cases are still considered and allowed for. ;) -- Ethan
It does feel like Perl, but, what if there was a keyword after the symbol? It'd be more readable and not Perl-ish, but, it wouldn't confuse the parser(or at least I wouldn't think it would).
Antoine Pitrou
Le Sat, 3 Aug 2013 01:46:37 +1000, Nick Coghlan
a écrit : In this context, you usually have one or two callbacks (two in Twisted-style programming: one for success, one for failure), passed positionally to a consuming function:
loop.create_connection((host, port), @cb, @eb) where: def cb(sock): # Do something with socket def eb(exc): logging.exception( "Failed connecting to %s:%s", host, port)
We can't use 'where' because we know it conflicts with the SQL sense of the term in too many APIs. We're reasonably sure we can get away with "given" without too much conflict, though.
How about reusing "with"? There's no ambiguity with context managers since the syntactic context is different.
Using "@" as the marker character is also problematic, since the following degenerate case will probably confuse the parser (due to it looking too much like a decorator clause):
@something() given: ...
No, that would simply be forbidden. In this proposal, "@" can only mark names of parameters in function calls. We already reuse "*" and "**" for a specific meaning in front of function call parameters, so there's a precedent for such polysemy.
I liked the notion of "?" as suggesting doubt and uncertainty - an element of "leave this undefined for now, we'll fill it in later".
I don't really like it :-) "?" has other meanings traditionally: as part of the ternary operator in C-like languages (many of them), as a wildcard character in pattern matching languages, as a marker of optional matchers in regular expressions.
Also, I really don't like the idea that "?" represents a full-blown object with attribute access capabilities and whatnot. It smells too much like Perl-style (Ruby-style?) magic variables. My proposal is more limited: it's a syntactic addition, but it doesn't create new runtime objects or types.
Regards
Antoine.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- Sent from my Android phone with K-9 Mail. Please excuse my brevity.
On 08/02/2013 07:48 AM, Nick Coghlan wrote:
On 1 August 2013 09:17, Andrew Barnert
wrote: But anyway, I think you're mostly agreeing with me. When neither lambda nor def feels right, it's usually not because you really want a multi-line expression, or a multi-line anonymous function, but because you want to get the petty details of the function "out of the way" of the important code, right?
Yeah, I've been grappling with this problem for years, which is why I have two competing deferred PEPs about it :)
For certain kinds of problem, the natural way to think about them is as "I want to do X", and as an incidental part of doing X, you need to define a function that does Y. Sorting, complex comprehensions, various flavours of event driven programming (especially GUI programming, where the yield-driven approach of PEP 3156 may not be appropriate, as well as the low level transport code for PEP 3156 style systems). Lambda isn't a great solution because it embeds all the complexity of the function directly in the main expression, obscuring the fact that the overall operation is "do X".
I'm not sure how much a function adds to complexity. Generally they simplify things by moving the complexity to within the function. But short one off functions are a different thing. In most cases they are in the following situations: 1. Reuse a small bock of code over and over again locally. 2. Capture a value now, to be used later with that code block later. 3. Be sent into or out of a context so it can be used with values that can't be accessed locally or presently.
Using up another precious symbol would be a big call, but it's starting to feel more like something of sufficient power to justify new syntax.
My feelings is that this type of thing requires a lower level solution, rather than a higher level abstraction. All of the above cases could be handled nicely if we could define a code block without a signature and call it with separately defined dictionary. It might look something like... seq = [] for i in range(10): ns = dict(i=i) co = def: i seq.append(ns with co) assert [expr() for expr in seq] == list(range(10)) It could be shortened to... seq = [({'i':i} with def:i) for i in range(10)] assert [expr() for expr in seq] == list(range(10)) The 'def:..' expression could return a code object. Which by itself wouldn't be callable. To make it callable, you would need to combine it with a signature object, or a name space. (Some sanity checks could be made at that time.) A signature object would return a name space constructor. This part is the part we don't need in many cases. (*) So being able to use an already constructed dictionary (or a yet to be constructed dictionary) as a name space has some advantages. The 'with' keyword is used here to combine two objects together. At least one of them would need to know what to do with the other. In this case it's the dictionary object that knows how to take a code object and return a callable frame like object. Probably it has a __with_code__ method. It may be too general of a definition of 'with', but possibly that can also be a good thing. If it always results in a callable object, then it would have just enough consistency to be easy to figure out when you come across them later without being too restrictive. But this part is just an option as a dictionary could just have a method.. callable_frame = {'i':i}.callable_with(def: i) Cheers, Ron
How about something like:
f = indef(x, y): print x, y
It's essentially storing lambdas. The syntax was taken from http://blog.deliciousrobots.com/2010/3/24/a-simple-compiler-with-python-and-.... indef stands for 'inline definition'. I've never heard someone use that name before. Or you could do 'slambda' for 'stored lambda'.
Musical Notation
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all. In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}" Python (proposed): def func(a,b): print(a+b) return a+b
becomes
func=function a,b: print(a+b) return a+b
------------------------------------------------------------------------
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- Sent from my Android phone with K-9 Mail. Please excuse my brevity.
On 31 Jul 2013 04:48, "Ronald Oussoren"
On 30 Jul, 2013, at 20:30, Ryan
wrote: How about something like:
f = indef(x, y): print x, y
How is that different from a lambda?
And, since assignments are statements, different from def? Cheers, Nick.
Ronald
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 07/30/2013 05:22 PM, Nick Coghlan wrote:
On 31 Jul 2013 04:48, "Ronald Oussoren"
mailto:ronaldoussoren@mac.com> wrote: On 30 Jul, 2013, at 20:30, Ryan
mailto:rymg19@gmail.com> wrote: How about something like:
f = indef(x, y): print x, y
How is that different from a lambda?
And, since assignments are statements, different from def?
Or the old "let" statement. While writing a little bytecode interpreter in python, (To try some ideas out.), I found out it's much easier to parse source code if a line starts with a statement keyword such as 'let' or 'def'. And I suspect is why early languages followed that pattern. Cheers, Ron
On Jul 30, 2013, at 11:30, Ryan
How about something like:
f = indef(x, y): print x, y
It's essentially storing lambdas.
You can already store lambdas. Or, more precisely, you can store functions. A lambda isn't a type; it's just a different way of creating functions, exactly the same type of functions you get from the def statement. And functions are first class values that can be bound to a name just like any other value. Meanwhile, the only advantages of lambda over def are that you don't have to come up with a name, and you can use it in an expression. So, trying to come up with a statement for giving lambdas names implies that there's something fundamental you're missing that would make your life a lot easier. Maybe you didn't know you could use def locally? Or you're trying to build code out of eval-ing strings because you don't know about closures? Or... Well, there are lots of possibilities. If you explain what you want to do that you think your new syntax would help with, I'm 99% sure Python already has a better way to do it.
I believe that anything that can not be expressed as a Python anonymous function must be a def. It is possible to express conditionals and loops within a lambda statement, if that is what you are looking for:
hidden = [ file for file in os.listdir(givenpath) if file.startswith(".")] \ ..: if isdir(givenpath) \ ..: else [givenpath.startswith(".")
BTW, if a multi-statement anonymous function syntax was to be considered
seriously, I'd recommend a lambda statement with colon replaced with a
brace-delimited block, which would barely cause code written for an
interpreter lacking it get refused:
server = nodeishServer(
lambda req, res {
res.writeHead(200, ContentType= "text/html");
res.end("Hello");
}
)
In fact, grammar for every statement which introduces a new block (if,
def, for, with, lambda) can be altered such that if the statement ends
with a `:' (semicolon), following lines are parsed as in usual Python
syntax, or, if the statement ends with a `{' (left brace), following
lines are parsed with non-indentation defined, C-ish syntax. So:
def fibonacci(n):
x, y, z = 1, 1, 0
for i in range(1, n):
z = x
x += y
y = x
return x
could be also written as
def fibonacci(n) {
x, y, z = 1, 1, 0;
for i in range(1, n) {
z = x;
x += y;
y = x;
}
return x;
}
which is a) subject of a different thread, and b) ridiculous.
-gk
On 30 July 2013 18:19, Musical Notation
Yes, I know that multiline lambda will never be implemented in Python, but in many languages it is possible to write an anonymous function without using lambda at all. In JavaScript: Instead of "function <name>(<variables>){code}" you can write "var name; name=function(<variables>){code}" Python (proposed): def func(a,b): print(a+b) return a+b
becomes
func=function a,b: print(a+b) return a+b
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 01/08/13 13:24, Göktuğ Kayaalp wrote:
BTW, if a multi-statement anonymous function syntax was to be considered seriously, I'd recommend a lambda statement with colon replaced with a brace-delimited block
You can get an idea of how brace-delimited blocks are treated by using a __future__ directive. At the interactive interpreter: from __future__ import braces and then take it from there. -- Steven
participants (17)
-
Andrew Barnert
-
Antoine Pitrou
-
David Mertz
-
Ethan Furman
-
Göktuğ Kayaalp
-
Haoyi Li
-
Masklinn
-
Matthew Lefavor
-
Michael Walter
-
Musical Notation
-
Nick Coghlan
-
Paul Moore
-
Ron Adam
-
Ronald Oussoren
-
Ryan
-
Steven D'Aprano
-
Terry Reedy