<div dir="ltr">[Steven D'Aprano]<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="gmail-im" style="color:rgb(80,0,80)">> The revelation that it's a function should come when you read the "by" or<br></span><span class="gmail-im" style="color:rgb(80,0,80)">> "key". </span><span class="gmail-im" style="color:rgb(80,0,80)"><br></span>I disagree. The most important fact is that it is a function, not <br>specifically what it does.</blockquote><div><br>I was trying to say that the context almost always gives away that the reader should expect a function.<br><br>Again, the pseudo code:<br><br><font face="monospace, monospace">hand = sorted(cards, by=card.suit)<br></font><br>Is usually enough for most people to understand. When you add in what the computer needs to make the whole thing mechanically unambiguous:<br><br><font face="monospace, monospace">hand = sorted(cards, key=lambda card: card.suit)</font><br><br>You can see the noise added compared to the pseudo code. The difference (to me) looks like this:<br><br><font face="monospace, monospace">hand = sorted(cards, by#=#############card.suit)</font><br><br>Ideally, we could move that noise out of the way so that the intent is more clearly expressed:<br><br><font face="monospace, monospace">hand = sorted(cards, by=card.suit ##########</font><br><br>Functions, variables and parameters are normally named such that they give away lots of context (or they should be).<br>Context that's available to the reader but not the computer.<br><br>[Steven D'Aprano]<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Consider:<br><font face="monospace, monospace">widget.register(value[a](x)         with x)</font> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>At first it looks like you are evaluating value[1](x) eagerly, right <br>there in the method call, and then you have to backtrack and change your <br>expectation about what you just read when you get to the end and see the <br>declarations.</blockquote></div><div><br>First, how is your example any worse that the delayed binding of generator expressions?<br><br><font face="monospace, monospace">widget.register(value[a](x)        for x in things)<br></font><br>Of course the context of what they're reading builds as they read it. You could put those spaces anywhere:<br><br>widget.register(value[a]            (x) for x in things)<br><br>Delayed binding works because human readers can deal with a little ambiguity in what they're reading especially if it means<br>putting the intent of the code before the book keeping.<br><br>If we're assuming a naive reader who's never seen the  <font face="monospace, monospace">widget.register </font><font face="arial, helvetica, sans-serif">method and method and variable names that</font><br><font face="arial, helvetica, sans-serif">are pretty ambiguous (not unheard of, especially in anonymous functions) then I would say the blocker is not knowing what</font><br><font face="monospace, monospace">widget.register</font><font face="arial, helvetica, sans-serif"> is in the first place. If you don't know that, then what point is continuing to read? Will knowing the kinds</font><br><font face="arial, helvetica, sans-serif">of object being passed clear everything up?</font><br><br><font face="monospace, monospace">widget.combobulate(5, "elephant", True)<br></font><br>Once the reader has encountered <font face="monospace, monospace">widget.register</font> they'll know it takes a function as an input. Just like anyone who's<br>used time.sleep will be thrown for a loop as soon as they see lambda in:<br><br><font face="monospace, monospace">time.sleep(lambda x: value[a](x))</font><br><br>[Steven D'Aprano]<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Given some sort of look-ahead, it's *possible* to put the parameter list <br>at the end of the function body:<br>    def function:<br>        do_this(a)<br>        do_that(b)<br>        do_something_else(c)<br>   with (a, b, c=None)<br>but I think you can see why that would be annoying.</blockquote><div><br>Yes! Common ground! <doing my common ground dance><br><br>Named functions serve a different purpose than anonymous functions.<br>They usually handle situations where the interface is more important than the implementation.<br><br><font face="monospace, monospace">def square_root(x):<br>    ...</font><br><br>Better spit out the square root of whatever number I give it. I don't care how.<br><br>Anonymous functions are almost always used in contexts where the interface is implied and the important bit is *what* it does:<br><br><font face="monospace, monospace">ui_element.on_mouseover(<what to do> with event)<br></font></div><div><font face="monospace, monospace"><br></font>[Steven D'Aprano]<font face="monospace, monospace"><br></font><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">You don't even know <br>which names are global and which are parameters until you get to the <br>very end of the function. Blah.<br>The same applies to function expressions. Given the function body:<br>    value[a](x)<br>which of value, a and x are global names and which are parameters?</blockquote><div><br>The same could be said of the <font face="monospace, monospace">value[a](x)</font> in:<br><br> <span style="font-family:monospace,monospace">widget.register(value[a](x)        for x in things)<br><br></span>[Steven D'Aprano]<span style="font-family:monospace,monospace"><br></span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">"Wait wait wait!" should ideally never happen. In programming, surprises <br>are not a good thing, and they're even less good when they are <br>retroactive. </blockquote><div><br>It happens when someone has never seen the <font face="monospace, monospace">map</font> function used or <font face="monospace, monospace">sorted</font> used with a key function.<br>It happens during the time where reading code is hard to begin with. The "wait wait wait" happens when<br>you don't know what <font face="monospace, monospace">map</font> is. Once you learn that, then you learn that the first parameter is a function.<br>Then whenever you see <font face="monospace, monospace">map</font>, you should expect the first parameter to be a function.<br><br>No amount of broadcasting that the first parameter is indeed a function will cure the confusion of not knowing what <font face="monospace, monospace">map</font>, or <font face="monospace, monospace">wiget.register</font> is.<br><br><b>Having said all that:</b><br>Jonathan Fine pointed out that the <EXPRESSION> <SEPARATOR> <SIGNATURE><br>format that I've been championing (where I've been using "with" for the separator) has a subtle flaw where<br>empty signatures are pretty awkward:<br><br><font face="monospace, monospace">d = defaultdict(100 with)</font><br><br>It *kind-of* works with "def" and you read it as, "the preceding expression is deferred" followed by an *optional* signature declaration.<br><br>d = defaultdict(100 def)<br><br>But it's not great. Another alternative is to use some (ideally short) prefix and making the signature declaration optional:<br><br><font face="monospace, monospace"><PREFIX> <EXPRESSION> [<SEPARATOR> <SIGNATURE>]<br><br>d = defaultdict(def 100)<br></font><br><font face="monospace, monospace">hand = sorted(cards, by=def card.suit with card)</font><br><br>of course there are many possible choices for the prefix and separator and subtle alterations like:<br><br>hand = sorted(cards, by==>card.suit with(card)) . # where '=>' is the prefix, it just blends nicely<br><br>or:<br><br>d = defaultdict(100 with())<br><br>Anyway, none of that interests me. What interests me is the recognition of *why* expressionization of statements often<br>leads to a re-ordered version of the original. My thesis is that expressions are slightly more flexible than statements which<br>allows for a more expressive ordering that is sometimes better for readability.<br><br>I think it's important to understand that if we ever expressionize other statements like try-except, with, etc.<br><br></div></div></div></div>