in reply to Re: Implementing (elisp-like) buffers in Perl 6: how to do buffer-localisation of arbitrary package variables? in thread Implementing (elisp-like) buffers in Perl 6: how to do buffer-localisation of arbitrary package variables?
I can assure you that the only reason why eLisp uses dynamic scoping instead of lexical is that it was designed before lexical scoping became common-place in the Lisp world, and nobody wanted to face the backwards-compatibility nightmare of changing it. If it had been invented a few years later, it would have used lexical scoping from the get-go. And this would have been a Good Thing.
So why repeat design decisions which are now thought to be mistakes?
Re: Implementing (elisp-like) buffers in Perl 6: how to do buffer-localisation of arbitrary package variables?
by jonadab (Parson) on Mar 31, 2003 at 04:28 UTC
|
Lexical and dynamic scoping are both useful, but they
serve different (indeed, totally unrelated) purposes.
Lexical scoping is for avoiding namespace collisions.
Package scope in general serves this purpose also,
at a different level.
Dynamic scope is not useful for avoiding namespace
collisions, but it has other uses, things lexical
and package scope do not do (directly as such).
Dynamic scope allows for control-flow-based values,
so that a variable can temporarily hold a new value
for a while, and then revert to the old one.
It is possible to get
around a lack of dynamic scope by passing lots and
lots of parameters in every single function call,
but in many cases that's cumbersome and inefficient.
(I don't mean inefficient with computer resources;
the compiler can probably optimise a lot of it away,
and anyway dynamic scope uses some resources too;
I mean inefficient with programmer time.) It's also
possible to get around it with closures or object
structures, but sometimes dynamic scoping is the
most straightforward way to do it.
I was not aware
of any serious computer scientist thinking that
implementing dynamic scoping was a mistake; there
are certainly plenty who think _not_ implementing
lexical scoping is a mistake, but that's an entirely
separate issue. Neither type of scoping is
substitutable for the other; they do very different
things.
Assembly language programmers have been using both
kinds of scope since the beginning of time; lexical
scope is when you store a value at a certain memory
address and only use it in one little segment of your
program. Dynamic scope is when you push the value
from a certain memory address onto the stack, do
some stuff, then pop it back off into the same place
it came from. It is highly impractical (some would
say impossible) to write a program of any substantial
complexity without in some fashion or another
doing both of these things. It's just a question of
whether the language and/or the programming environment
provides a direct mechanism or whether the programmer
has to make special arrangements. (I already listed
some ways to work around a lack of dynamic scoping.
You can also work around a lack of lexical scoping,
by giving your variables unique names. A lack of
package scope is more of a pain, but this can be
worked around too, by simply including the name of
the package at the start of the name of every variable.
This makes for verbosity, but it works in a pinch.)
Anyway, Perl6 is going to have dynamic scope one way
or the other (though the misleading keyword "local" is
being changed to "temp", which makes sense); all I'm
thinking to implement is buffer scope (which would be
used for the same sorts of things as dynamic scope,
albeit in different situations, much as package scope
and "my" lexical scope are used for the same thing
in different situations). Perl6 also introduces
something called hypothetical scope, which adds yet
another semantic; it is not equivalent to either
lexical or dynamic scope, but is its own thing, or
perhaps a sort of hybrid.
for(unpack("C*",'GGGG?GGGG?O__\?WccW?{GCw?Wcc{?Wcc~?Wcc{?~cc'
.'W?')){$j=$_-63;++$a;for$p(0..7){$h[$p][$a]=$j%2;$j/=2}}for$
p(0..7){for$a(1..45){$_=($h[$p-1][$a])?'#':' ';print}print$/}
| [reply] [d/l] |
|
The purposes that they serve are not so different. In fact in many languages - including every version of Lisp before Scheme and Perl before my was introduced in Perl 5 - dynamic scope was routinely used for what we now tell people to use lexical scope for.
Admitted, there are differences. And there are times when you really do want dynamic scope. But those are few and far between. The times that I have used it are for locking logic (in which case straight dynamic scope does not do the job, I need the dynamic thing to be visible outside of my program) and once for a deep recursion test (you can use local on the values of a hash!). I could have survived without built-in dynamic scope for both cases, but would have had to use exception handlers to do it safely. (You need to catch every way of exiting the dynamic scope...)
As for the idea of having a ton of custom variables which can be accessed from anywhere that contains state, if your programs look like that then you probably could stand to learn something about good program design IMO. A multiplication of global or semi-global variables is a red flag for a bad design. (Geez, can I sound more arrogant?)
| [reply] |
|
In fact in many languages - including every version of Lisp before Scheme and Perl before my was introduced in Perl 5 - dynamic scope was routinely used for what we now tell people to use lexical scope for.
That would be a grave mistake. If
that is true, then I can certainly see why you would
be hesitant about the use of dynamic scope; it is
clearly inappropriate for avoiding namespace clashes.
That wouldn't work very well at all; there would be
all sorts of hard-to-debug problems if it were used
for that. However, that doesn't mean dynamic scope
isn't useful, or even that it's less useful than
lexical scope; it only means it's not useful for the
same kinds of things.
And there are times when you really do
want dynamic scope. But those are few and far
between.
I can't agree with that. When you understand dynamic
scope properly (i.e., are using it for what it's good
for, not as some kind of warped substitute for lexical
scope), it's very useful indeed.
If you need to simulate lexical scope in a language
that doesn't support it directly, the right way to
do that is by naming convention (i.e., name your
lexical variables starting with the name of the
package (and possibly routine) that contains them),
not by misusing dynamic scope.
But it's also not appropriate to try to use lexical
scope as a substitute for dynamic scope; that would
be at least as hideously inappropriate, if it could
even be made to work at all.
And no, most of the time you aren't going to be
dynamically scoping lots and lots of variables.
But the ones that you do need to scope that way,
you need to scope that way. As
you point out, trying to work around a lack of
it is nightmarish. Yeah, it can be done -- in the
same sense that recursion can be done in languages
like line number BASIC that don't support passing
parameters to subroutines. (I did this once. It
was kinda fun, actually, in a perverse sort of way.
I used a set of parallel arrays to simulate a stack,
which I pushed my values onto before GOSUB (to save
them) and then popped them off of after the routine
RETURNed. It was messy, though, and fun only as
an exercise, and I don't want to do it again.)
for(unpack("C*",'GGGG?GGGG?O__\?WccW?{GCw?Wcc{?Wcc~?Wcc{?~cc'
.'W?')){$j=$_-63;++$a;for$p(0..7){$h[$p][$a]=$j%2;$j/=2}}for$
p(0..7){for$a(1..45){$_=($h[$p-1][$a])?'#':' ';print}print$/}
| [reply] [d/l] |
|
|