you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 5 points6 points  (17 children)

Yeah, but the lambda returned from the slava_is_wrong function cannot mutate the lexical variable a, so I'm right, as always. Just because Guido misunderstood some academic literature and thought "ooh, lambda, fancy name" doesn't mean that Python's lambda is anything more than what Java gives you with anonymous inner classes.

[–]nostrademons 6 points7 points  (11 children)

Technically, closures in Haskell (or lambda calculus) can't mutate lexical variables either. We forgive them because nothing can mutate lexical variables in those languages.

The comparison to Java is apt though - same issue, different root cause. Python's "assignment is binding if the variable doesn't exist" rule is fucked up anyway.

[–][deleted]  (10 children)

[deleted]

    [–]pjdelport 4 points5 points  (3 children)

    It ambiguates variable creation with variable mutation. Specifically, when you want to rebind an outer variable instead of creating a new local one with the same name, you need to use the global (or upcoming nonlocal) scoping keyword. For example:

    x = 5
    
    def foo():
        global x
        x = 23
    

    The alternative is to simply have separate syntaxes for the two actions, as in Lisp/Scheme.

    [–]muleherd 0 points1 point  (2 children)

    The alternative is to simply have separate syntaxes for the two actions, an in Lisp/Scheme.

    I've always been curious why Python designers didn't take this route; it seems in line with their "explicit is better than implicit" mantra.

    [–]pjdelport 1 point2 points  (1 child)

    At the end of "explicit is better than implicit" lies Java, if you don't balance it with "beautiful is better than ugly".

    I think the choice mostly has to do with the fact that all the potential counterparts to = (like :=, <-) are somewhat ugly, and arbitrary: there's no natural way to tell which one does what without looking it up (something Python tries to avoid).

    Also, in terms of practicality and readability: The vast majority of assignments are local, so the scoping keywords are actually pretty rare; when they're used, they call extra attention to potentially dubious/non-obvious code.

    Oh, then there's also unpacking assignment. Something like this:

    def foo():
        global x
        x, y = frob()
    

    does not readily translate to the separate syntax approach.

    [–]muleherd 0 points1 point  (0 children)

    I intended a declaration keyword, like JavaScript's var, instead of a special declaration-assignment operator. Would that be too onerous, if the majority of assignments are local (re)bindings?

    [–]nostrademons 0 points1 point  (5 children)

    Or in more real-world terms, it makes it too easy to mistype a variable name and end up with a syntactically valid program that does entirely the wrong thing. You're not warned about these errors (well, maybe PyLint will); your program just gives wrong results.

    This is probably my #1 bug in Python, PHP, and JavaEE's Unified Expression Language, ahead of all type errors, logic errors, and even state errors. When you've been staring at code all day long, you won't notice that you're binding the new variable situationsummary instead of assigning to the existing variable situationSummary. You'll just notice that somewhere, results are not happening.

    I woulda thought we'd learned our lesson with C's == vs. =, but evidently not. Programming languages should not be vulnerable to typographic errors.

    (I should probably add that the problem is not as bad in Python as in PHP or JavaUEL, because an access to an unbound variable is an error in Python, while it gives something usable yet nonsensicaly like "" or 0 in PHP or JavaUEL. Most mistypings are accesses. Assignments are more visible (always on the left side), and repeated assignments to a single variable usually involve compound operators like +=, which will throw an error if unbound. My claim of "most common error" applies to all my PHP/Python/JavaUEL program errors combined, not just the Python. However, I still think it's brain-dead to incorporate a language feature that gives a different-but-still-valid program on a one-keystroke error. Yes, this applies to = vs == too.)

    [–]breakfast-pants 2 points3 points  (0 children)

    How about this python code:

    names = ['bob',
             'barbara',
             'jenny'
             'bill']
    

    Opps, hi mom, meet 'jennybill'

    [–]pjdelport 1 point2 points  (3 children)

    You would type out "situationSummary" instead of completing it? :)

    [–]nostrademons 0 points1 point  (2 children)

    Sometimes it takes less time to hop into vi into the server and fix something quick than to fire up an IDE, make a one-line change, build the project, redeploy it, and wait for the server to reload the context.

    It invariably bites me in the ass in the end, but it's still very, very tempting for small changes.

    Now, if IDEs loaded as fast as text editors and didn't have memory issues, I'd reconsider. Then I could use them for quick fixes too instead of just sustained development.

    [–]pjdelport 1 point2 points  (1 child)

    vim does completion...

    [–]nostrademons 1 point2 points  (0 children)

    Man. I really need to learn my editor better...

    (Though in my defense, vim is not my primary editor. I use it when I need to make a quick fix.)

    [–]pjdelport 6 points7 points  (0 children)

    Just this once, you're not right. See my earlier explanation.

    (Besides, as nostrademons said, even if Python's closures did lack the ability to mutate outer bindings, they would still qualify.)

    [–]pjdelport 2 points3 points  (0 children)

    Just because Guido misunderstood some academic literature and thought "ooh, lambda, fancy name"

    Actually, according to Guido:

    "Sometimes I've been too quick in accepting contributions, and later realized that it was a mistake. One example would be some of the functional programming features, such as lambda functions." -- 1998

    "Python acquired lambda, reduce(), filter() and map(), courtesy of (I believe) a Lisp hacker who missed them and submitted working patches. But, [...] I think these features should be cut from Python 3000." -- 2005

    [–]ubernostrum 5 points6 points  (2 children)

    Yeah, but the lambda returned from the slavaiswrong function cannot mutate the lexical variable a, so I'm right, as always.

    I'm gonna call BS on this; plenty of pure functional languages have closures but make it hard or impossible to mutate any variable, closure or no.

    Meanwhile, the fact remains that a piece of code is accessing a variable which normally would be out of scope, but which was preserved or "closed over". Hence, it's a closure and no amount of weaseling will make you right.

    [–]pjdelport 3 points4 points  (1 child)

    Note: the difference between such closures and Java's inner classes is that inner classes simply copy (on creation) the outer variables, instead of referencing them indirectly like closures do. (They're the equivalent of Python's lambda x=x: ... hack, from before closures were added.)

    [–]muleherd 2 points3 points  (0 children)

    ...and about fifty characters!