3 ms·
Yes, although it gets trickier with mutation. For example: x = 0 foo = function(){ x += 1 } bar = function(){ x -= 1 } Somehow you need to update
by jules 16y ago
Yes, although it gets trickier with mutation. For example:
x = 0
foo = function(){ x += 1 }
bar = function(){ x -= 1 }
Somehow you need to update bar's x too when you call foo and foo's x when you call bar. One way to do this by wrapping x in an object of its own, for example in a single element array that just contains x. I think the implementation of closures in Mono got this wrong, calling foo would have no effect on bar and vice versa (perhaps it's fixed now).
- ww520 16y agoI was hoping to keep thing nice and simple. :) You brought up a good point in the subtle problem in using closure. Yes, in some implementation of closure, both foo and bar access the same closed variable x. The best way to treat this is to view closed variables as global variables in different scopes. Global variables imply they are shared. e.g. x = 1 // environment in the global scope function baz() // environment in baz's scope shared by foo and bar x = 3 foo = function() { x += 1 } bar = function(){ x -= 1 } return [foo, bar] foo1, bar1 = baz() // create a baz_env1 [x:3] bar1() // baz_env1 [x:2] foo1() // baz_env1 [x:3] foo1() // baz_env1 [x:4] foo2, bar2 = baz() // create a baz_env2 [x:3] bar2() // baz_env2 [x:2] x = 10 // At this point, // global environment's x is 10 // baz_env1 x is 4 // baz_env2 x is 2 Hope that clarifies things.
- jacquesm 16y agoI've described a closure to myself as 'a temporary freeze on the scope and all variable bindings as they were when you got there the first time'. Is that correct?
- silentbicycle 16y agoYes - everything will act as if that stack frame was heap-allocated and still exists* . You can modify the value(s), define multiple functions sharing the same variable bindings, etc. A lot of common language features (such as conventional objects) can be implemented as syntactic sugar on top of closures. (Same with continuations.) * Compilers/interpreters are free to do optimizations that won't affect results, like only heap-allocating the closures that escape their scope.
- jacquesm 16y agoThanks!
- silentbicycle 16y agoWhen I'm struggling with what a programming concept means, I find it helpful to try implementing it in a toy interpreter. Efficiency is irrelevant, it just needs to model the concept correctly. Many concepts really aren't that complicated if you're not worried about optimization, and getting to a working model tends to shake out the aspects you don't completely understand. (This is also why books have exercises.)
- jacquesm 16y ago> When I'm struggling with what a programming concept means, I find it helpful to try implementing it in a toy interpreter. That's my 'I have understood' benchmark. If I can re-implement something without any reference to outside sources. My goal is to write a little lisp or scheme from the ground up from nothing but the lambda calculus and some bits & pieces. It'll be slow as molasses but it will help me to really grok it. That's why I've been reading up on all this stuff over the past year or so. After that I think I'm ready for something more involved, like clojure. I realize that this is not a very efficient way of learning but in the long run it tends to pay off. Knowing what goes on under the hood is an advantage.
- silentbicycle 16y agoYeah, it seems like the long way around, but it really adds up over the years! You'll probably want to get Quinniec's _Lisp in Small Pieces_, if you don't have it already. It covers several aspects of implementing Lisp interpreters and compilers, the lambda calculus, a bit on semantics, etc. (It mostly focuses on Scheme.)
- jules 16y agoYes, that's actually close to another way of implementing it. But you can't just blindly assign the scope of baz to foo and bar, because foo and bar might have local variables of their own. Another issue is that baz might have local variables that aren't used in foo and bar, so when baz returns you want the garbage collector to be able to reclaim that storage (otherwise it could lead to arbitrary space leakage if these variables point to large structures). You need a tree structure of environments in the general case.