Re: [Python-ideas] Proposal for Ruby-style anonymous block functions (that don't kill the indention)
On 2008/11/09, at 8:15 pm, Mike Meyer wrote:
On Sun, 9 Nov 2008 16:56:55 -1000 Carl Johnson
wrote: This list had a proposal last month to make everything an expression, and it has not infrequent attempts to create multi-line lambdas, and I think the reason for this is that people want a better way to create functions that act like Ruby-style blocks since sometimes it makes more sense to write the function that will be passed after the thing it will be passed to and not before. So, here's my proposal. I expect it to get rejected, but hey, someone approved @decorator, so maybe it will make it...
I think you may be on to something. You've identified the real cause of the reasons this kind of thing generates ugly code, and attempted to deal with them. But it needs some tweaking.
Instead of
def decorator(f): ... def inner(*args, **kwargs): ... print(*args, **kwargs) ... return f(*args, **kwargs) ... return inner ...
where the def inner comes before you know what it's going to be used for, why not
def decorator(f): ... return @(*args, **kwargs): ... print(*args, **kwargs) ... return f(*args, **kwargs) ...
You seem to have realized *part* of the problem, and attempt to deal with it here:
Caveats: This shouldn't be allowed to work with two @s in one line. If you have two of them, you should give them names ahead of time with def. This also should not be allowed work inside a for-, if-, with-, or while- statement's initial expression, since the indenting won't work out.
Yup, this makes lots of sense, and would seem to eliminate some simple ugly cases. But it doesn't deal with the real ugliness that comes with multiple things in a statement. This would look like a godsend to creating properties, where you need three functions that could all be anonymous, like so:
class Ugly(object): x = property(@(self): return self._x , @(self, value): self._x = value , @(self), "I'm the x property"): del self._x
Except that that's the simple case, and it border on unreadable all by itself.
Maybe @() shouldn't be allowed except for the last line in a multi-line statement?
Properties have already been fixed in Python 2.6+ as far as I'm concerned, but I don't think that should be a valid use of the @, even without my caveats, since it's mixing blocks around inside an expression. The expression should be terminated before the beginning of the block.
Also, myfunc = @(args) should be preemptively banned, since def is the one right way to do it. Also you shouldn't be allowed to do this to make one liners with this (as you can eg. with if- statements), since that's why there's lambda.
I disagree with both of these, mostly on symmetry grounds. I can already do:
lowerfunc = lamba word: word.lower()
why should this nifty new construct not be allowed to play? Similarly, every statement that is followed by a block can be followed by a simple statement, or a list of simple statements separated by semicolons. Why is this one different?
Mostly on grounds of TWOOTDI. I know people already consider "x = lambda:" to be poor style, so I want to ban bad style preemptively with @. Incidentally, does anyone know why for decorators, @lambda x: x def f(): pass is a syntax error? I guess people just don't want lambda based decorators, for whatever reason.
In particular, this fixes one of the properties of lambda's that people complain about most often: the inability to put statements in them. Why provide that win, only to basically make it worthless by forcing people it to use multiple statements? I mean, this:
newlist = sorted(words, key=lambda word, print word; word.lower())
would be great, but isn't allowed because lambda is restricted to an expression. You fix it by giving us:
newlist = sorted(words, @(word)): print word; return word.lower()
only to turn around and day "No, this block is special, and you can't do that here."????
I think it would look better on four lines. Maybe I'm wrong, but that's my intuition.
And using this on the declaration line of a decorator is just crazy.
Why? One of the most powerful languages I know of came about because the designers let people do "crazy" things. Sure, using a @( construct for the decorator expression is sort of pointless, but what if you want to pass an anonymous function to a decorator. Why is:
@magicdecorator(lambda word, word.lower()) def function(...
ok, but:
@advancedmagicdecorator(@(word)): return word.lower() def function(...
not ok?
Ah, to be honest, I hadn't thought it through and just assumed it was impossible to make it work, since there would be two things needing indent-dedent tokens in a row. Still, isn't it a little ugly? (And remember, this proposal is not about adding power to Python, just making things more readable.)
I kinda like it, if you can figure out how to deal with things like property. On the other hand, it smells a lot like lambda, which some people would like removed from the language...
Agreed. I fully expect the BDFL to reject this proposal, but I still think it's interesting to consider as a "solution" to the insolvable problem of multiline anonymous functions.
http://www.mired.org/consulting.html Independent Network/Unix/Perforce consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
I actually like the idea. Having an @ in a statement turns that statement grammatically into the start of a block construct. But I prefer the easier way of allowing lambda x: print x; return x*x kind of syntax (which may require brackets around the lambda to disambiguate the semicolons), and that doesn't look like it's going to make it any time soon. I also agree with not liking something like myfunc = @(args): ..., but I don't think the grammar should be complicated with more special cases than necessary. Leave this to the style guide. One major technical problem with this is that it would stop the python grammar from being LL1, and I think Guido wants to keep it like that. In fact, I'm not quite sure this grammar is still context free (but I think it still barely is...) Jan
Jan Kanis wrote:
One major technical problem with this is that it would stop the python grammar from being LL1
Not necessarily. You could just allow any expression-statement to be followed by a block, and sort out whether it makes sense later on. -- Greg
On 14/11/2008, Greg Ewing
Jan Kanis wrote:
One major technical problem with this is that it would stop the python grammar from being LL1
Not necessarily. You could just allow any expression-statement to be followed by a block, and sort out whether it makes sense later on.
Hm, hadn't thought about it in that way. Doing so now, if we follow this train of thought to it's ultimate conclusion everything is LL1, just parse the file as a sequence of characters and sort out whether they make sense later on :) . Jan
Jan Kanis wrote:
if we follow this train of thought to it's ultimate conclusion everything is LL1, just parse the file as a sequence of characters and sort out whether they make sense later on :) .
You're right that this is a possible implementation strategy, but it doesn't mean that "everything is LL(1)". Being LL(1) or not is a property of the grammar, and it concerns the language that the *parser* accepts. The Python language itself is actually not LL(1). CPython gets away with using an LL(1) parser because the parser accepts a slightly different language that's a superset of Python, and *that* language is LL(1) (meaning that you can write down an LL(1) grammar for it). This is the strategy used by almost all practical language implementations, and usually the division of labour is arranged so that the parser does most of the hard work of weeding out invalid programs. In the case you suggest, you've gone to the extreme of making the parser do almost none of the work, and although the language the parser accepts is LL(1), it's a wildly different one from the language the system as a whole accepts. So when we talk somewhat loosely about a language such as Python being LL(1), what we mean is that there is a superset, which isn't *too* much bigger, that's LL(1), and we make our parser recognize that superset. -- Greg
participants (3)
-
Carl Johnson
-
Greg Ewing
-
Jan Kanis