Also note that combining
- the concept of lexical scope (which--we feel--is a nice thing for a programming language, as opposed to dynamic scope)
- with function definitions (lambda expressions) embedded deeply in the code (one could also call such definitions "nested functions" to be short)
can turn out to be a complex endeavor, both from the point of view of language implementation, and from the point of view of the programmer. There is even a special name for this complex thing: closure.
As Wikipedia writes:
Correct implementation of static scope
in languages with first-class nested
functions is not trivial, as it
requires each function value to carry
with it a record of the values of the
variables that it depends on (the pair
of the function and this environment
is called a closure).
This is not only non-trivial to implement in a langauge with global and/or mutable variables (like C or Java; think of ensuring a correct access at the moment of evaluation of a closure to the mutable state that was in scope at the place of the nested function definition! Just one thing: the used objects shouldn't have been destructed and garbage-collected when you evaluate the closure some time in the future), but also it is not trivial conceptually for a programmer to think about how the closure will work in a complex situation and which (side)-effects it will exactly have (for the same reason: you need to think about the interaction of the closure with all the mutable state that was in scope when you defined the closure, e.g.: When you refer inside a closure definition to an outer mutable variable that is in-scope, do you really want to access the value the variable had at the time of the definition of the closure, i.e., you want to have a read-only copy of the variable, or you want a full-blown access to the mutable state of the variable in the future at the time of an evaluation of the closure?).
In pure functional languages, it's much simpler to think about nested function definitions and their uses, and so having only lexical scope is not a problem for them at all. But if your language is not functional, it's not so trivial. (I believe it's one of the reason why it has been debated for a long time how to add closures to Java: they didn't seem to be trivial enough for a programmer to understand, although they just build upon the nice concept of lexical scope.)
Thinking about nested functions in non-purely functional languages is simpler with dynamic scope (although dynamic scope is not nice: you get less compile-time checks and guarantees about the correct behavior of your program with dynamic scope).
So I think the advantage of having dynamic scoping available in a language can also be the possibility to program some things in a simple way if one wants to and dares to do this given all the dangers of dynamic scope.
Notes
Regarding the long history of (no) closures in Java (and that the programmers didn't like the concept) -- http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg04030.html:
Date: Thu, 14 Aug 2003 08:05:44 -0700
From: Michael Vanier
Subject: Re: bindings and assignments (was: Re: continuations)
Date: Thu, 14 Aug 2003 10:45:34 -0400
From: "David B. Tucker"
I imagine, though I don't have statistical evidence, that the
requirement of declaring local variables to be final in order to
reference them within anonymous inner classes (closures) is almost
entirely unknown and unused in practice.
Out of curiosity, does anyone know why Java only allows final
variables
to be referenced from within anonymous classes?
Dave
<cynic>Otherwise you'd have the equivalent of true closures,
and if you had that java would be a
*really* powerful and useful language, so they obviously couldn't do that.
</cynic>
Actually, the prototype implementation
did allow non-final variables to be referenced from within inner classes.
There was an outcry from users,
complaining that they did not want
this! The reason was interesting: in
order to support such variables, it
was necessary to heap-allocate them,
and (at that time, at least) the
average Java programmer was still
pretty skittish about heap allocation
and garbage collection and all that.
They disapproved of the language
performing heap allocation "under the
table" when there was no occurrence of
the "new" keyword in sight.
So, in the early days--apparently--a "third" approach (as opposed to the two I have mentioned in my text above) was to be taken in Java: neither "read-only copies", nor real access at the time of the evaluation to the enclosing (at the time of the definition of the closure) mutable state, but rather mutable copies of the state (at least, I understand the quoted passage this way; or no, is he talking about heap-allocating just the references?.. Then it's the second option. Good. The third option really looks not sensible to me.). Not sure how they are implementing closures nowadays in Java, I haven't been following the new recent stuff about Java.