Re:Why Hy?
by contravariant on Tuesday May 03, 2022 @10:24AM
(#31248688)
What problems did you encoutner implementing 'let'? Shouldn't it be equivalent to lambda expressions somehow?
[ Parent
| on HN ]
Re:Why Hy?
by Kodiologist on Tuesday May 03, 2022 @10:35AM
(#31248845)
In 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.)For 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.Equivalent in Common Lisp:
* (let ((a 1))
(defun foo ()
(setq a 2))
(foo)
(print a))
2
2
Re:Why Hy?
by ashtonbaker on Tuesday May 03, 2022 @11:02AM
(#31249195)
this 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!It works if you disambiguate your namespaces:
>>> a = 1
>>> def foo():
... a = globals()['a']
... print(a)
...
>>> foo()
1
Still yuck?Here's another way to do it:
>>> def foo():
... import __main__
... a = __main__.a
... print(a)
...
>>> foo()
1I'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.
Is there any non-horror piece of code where supporting this latter case would be useful?
It 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.
it 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.