4 ms·
What problems did you encoutner implementing 'let'? Shouldn't it be equivalent to lambda expressions somehow?
by contravariant 5y ago
What problems did you encoutner implementing 'let'? Shouldn't it be equivalent to lambda expressions somehow?
- Kodiologist 5y agoIn some other Lisps, yes, but Python has its own idea of how function scoping works that generally isn't what you'd want with `let`. For example, in (let [a 1] (defn foo [] (setv a 2)) (foo) (print a)) you probably `2` to be printed, but the seeming equivalent Python code a = 1 def foo(): a = 2 foo() print(a) prints `1`. Python interprets the assignment as declaring a new local variable. Frankly, Python got scoping wrong (in its quest to eliminate declarations), but here we are. So `let` needs to work around this somehow. The current implementation implicitly adds `global a` or `nonlocal a` as appropriate. (Actually, the name `a` is also mangled so that any references to `a` outside the `let` refer to a different variable.)
- ashtonbaker 5y agothis made me wonder - I know I can use global variables inside a function definition: a = 1 def foo(): x = a foo() so if this 'a' is a new variable, what happens when the name collides with a global variable? a = 1 def foo(): a = a foo() > UnboundLocalError: local variable 'a' referenced before assignment yuck!
- all2 5y agoI'm not sure this is so much of a problem? Changing what you name the variable fixes the issue. Python is generally quite good at having a verbose option to express things, so I'm surprised there's not an explicit way to reference the enclosing scope from an object.
- vegai_ 5y agoIs there any non-horror piece of code where supporting this latter case would be useful?
- m55au 5y agoIt is not a particularly good example. A better one would be a = 1 def foo(): x = a a = 2 foo() > UnboundLocalError: local variable 'a' referenced before assignment Basically a name cannot be both global and local inside the same function. Otherwise it would be a total mess. "a = a" just means assign the value of local variable "a" to a local variable "a", so the error makes sense.
- gilch 5y agoIt works if you disambiguate your namespaces: >>> a = 1 >>> def foo(): ... a = globals()['a'] ... print(a) ... >>> foo() 1 Still yuck?
- gilch 5y agoHere's another way to do it: >>> def foo(): ... import __main__ ... a = __main__.a ... print(a) ... >>> foo() 1
- m45t3r 5y agoFor me the example printing `1` makes sense because I think `let` as being something immutable, so I wouldn't expect `setv` to mutate that variable (would expect either a compiler error or the introduction of a new local variable). But maybe it is because I am familiar with Clojure (where `setv` or equivalents doesn't exist) and not other Lisps. This would be the equivalent Clojure code anyway: user=> (let [a 1] (defn foo [] (def a 2)) (foo) (println a)) 1 nil So yeah, makes sense for me. But can understand why some other people would expect the other way though.
- gilch 5y agoEquivalent in Common Lisp: * (let ((a 1)) (defun foo () (setq a 2)) (foo) (print a)) 2 2
- baq 5y agoit kinda sorta works when wrapped in a function def f(): a = 1 def g(): nonlocal a a = 2 g() print(a) though nonlocal can't be used when referring to global variables... it's kinda bad.
- gilch 5y agoYou can still use `global` though.