This quote from the article packs quite a few misunderstandings into one sentence fragment:
[...] they allow you to create units
of work, which each have their own
copy of the stack, and don’t step on
each others toes as a result.
This is not generally true of languages with closures.
Firstly, more often they have references to the stack, not copies, for the sake of efficiency. And in the majority of languages, you can modify things through a reference. So in those languages, this feature does not provide a way of isolating units of work at all. If anything, it can make it more confusing.
Secondly, in most (sane) languages you can refer to anything that is lexically enclosing a local function. You cannot refer to just anything anywhere on the stack. For example, you cannot dig into the local variables of the function that called the function that called the function... etc. You can only access the variables/parameters of functions declared locally, whose text encloses the text in which the usage occurs. Also local variables and parameters ("on the stack") are not the only things that may be lexically enclosing a function. So "the stack" is the wrong concept to invoke here.
Java is one example of a language that can only take copies of local variables and parameters in its "anonymous inner class" closures. However, it will still take a reference in the case of the this
reference of the outer class.
And in the case of closing over this
, the inner class will now store an implicit reference to the outer class - nothing to do with the stack, really.
The situation is similar in C#, except local variables and parameters are captured by reference instead of being copied.
var counter = 0;
Repeat(10, () => counter++);
Suppose Repeat
is a library function that starts another thread and will now call the Action
lambda passed to it every 10 milliseconds. You can hopefully see how this is a very succinct way to create race conditions!
The only kind of language that would avoid this problem would be a pure functional language such as Haskell, but that would not be due to closures - clearly - but instead due to the fact that you can never modify any shared state. (And Haskell still wouldn't avoid the problem entirely; most real software has to interact with shared state external to the program at some point, and Haskell's standard library has a way of doing that).