3 ms·
So, there's one thing here that strikes me as really weird. And I think it's best summed up at the end, where it discusses closing over a loop variable. It di
by Sniffnoy 7y ago
So, there's one thing here that strikes me as really weird. And I think it's best summed up at the end, where it discusses closing over a loop variable.
It discusses how if you close over a loop variable, people expect that each function created will get a different value of the variable, even though there's only one variable, and closures close over variables, not values. It treats this as a special case. But to my mind this isn't a special case; it just highlights that having closures close over variables rather than values is, well, not right. Like, if I write, to use some sort of pseudocode,
x = 1;
f = function() { print(x) };
x = 2;
g = function() { print(x) };
...then I expect that f will print 1 and g will print 2. Because, well, I'm coming in from lambda calculus and Haskell where everything is values. Of course, there this problem doesn't come up because there's no mutability -- but that's still how I'd expect things to work once you introduce it. Closing over variables just seems horribly counterintuitive to me.
This came up earlier, too, when the was going through all the machinery with upvalues, and I was just like, why doesn't it just copy in the values? And after reading further, it's like, oh, it's because they want to be able to modify the variable outside the function! Which, like, whoa.
Like obviously what I said above about how I expect closures to work isn't workable if you want to be able to mutate the closed-over variables, outside the closure, from inside the closure. But maybe that just shouldn't be allowed? I think if I were writing a language from scratch, and it included lambdas, they'd close over values, not variables, and mutating the closed-over variables would have no effect on the world outside the closure (or perhaps be disallowed entirely).
So, basically... are you sure that people expect that behavior with loop variables because they are thinking of each iteration as a new variable? Or is it because they don't expect closures to close over variables at all, but rather values, and would be surprised that loop variables are being treated as a special case rather than closures just always working that way? Because in my case it's definitely the latter.
- chrisseaton 7y agoI think some languages let you choose to capture variables or values - does C++ do that?
- FreeFull 7y agoC++ and Rust do let you capture by value. Here is a Rust example that outputs exactly what you would naively expect. fn main() { let mut x = 1; let f = move || println!("{}", x); x = 2; let g = move || println!("{}", x); f(); g(); }
- Gibbon1 7y agoSort of related with gcc's nested function extension, you can reference variables that in scope when the function is defined. If you pass a pointer to the nested function is can access those variables (via a trampoline on the stack). To me that's been very useful to have both to get around not being able to use malloc. Meaning I think it's a good idea actually.
- ceronman 7y agoFrom a functional perspective, this of course doesn't make any sense. But from an imperative perspective it does. A common use for closures in imperative languages is to have custom control structures similar to `if`, `for`, `while`, etc. And in imperative languages mutating values is very important, if you want a new control structure you have to be able to mutate your surrounding variables. Smalltalk only uses this kind of control structures based on closures. Ruby also uses this a lot, it even changes the meaning of `return` in closures (blocks) to return not from the closure but the enclosing function.
- ufo 7y agoThis mechanism that you suggest about copying values is how Lua used to work before version 5.0, when they came up with the current upvalue mechanism.