Ask HN: What makes dynamic scoping different from all variables being global?
Is dynamic scoping really a nicer word for "no scoping"? (Puns about first person shooter games aside.)
Is dynamic scoping really a nicer word for "no scoping"? (Puns about first person shooter games aside.)
http://www.lispworks.com/documentation/HyperSpec/Body/m_defp...
const x = 'lexically scoped';
const f = () => x;
const g = () => {
const x = 'dynamically scoped';
return f();
};
console.log(g());
It prints which scoping Javascript uses. When `f` is executed, `x` isn't defined in that scope (the body of `f`), but `x` is used in that scope: does the language look in the scope that `f` itself was defined in (the top-level in this case, but you can have nested scopes), or does it look, at run-time, at the scope that `f` was called from (the body of `g`)? That's the difference. The local-to-`g` `x` that dynamic scoping would use when `f` is called is not a global; it's an entirely different variable from the other `x` (even in a dynamically scoped language, calling `g` won't change the outer `x`'s value).Dynamic scoping can be useful: for example, in Common Lisp (which supports both lexical and dynamic variables), STANDARD-INPUT is a dynamically scoped variable, so you can temporarily redirect stdin like:
(with-open-file (*standard-input* (open "file.txt"))
(function-that-reads-from-stdin))
And not have to worry about setting STANDARD-INPUT back to its old value when you're done. Scala's implicits are also often used to produce the same effect as dynamic scope.As an aside, you could argue that `this` is dynamically scoped in Javascript (outside of new lambdas).
Dynamic scoping means that the expression X refers to the innermost binding of X in the entire activation chain ("call stack") leading up to that point (or possibly to a global, top-level X if there is no such binding).
Global means that X refers to a single instance in a top-level environment.
Dynamic scoping can be faked with global variables plus a hidden saving/restoring mechanism. That is to say, the local binding of a dynamic variable can be simulated by saving the current value in a hidden local location associated with the activation frame, then simply assigning to the global variable, and finally restoring the prior value (with unwind protection, so it is guaranteed). However, the ruse will be exposed under any sort of concurrency (threads, coroutines, continuations) which will make it apparent that there is only one instance of the variable. Under a true implementation of dynamic scoping, the local binding of the dynamic variable actually has a separate local location from the fall-back global one.
Note that the scoping classification of bindings is not sufficiently nuanced to capture what is possible. Words like "lexical" refers to visibility but "binding" is what happens at run-time: the establishment of the life of the thing that the name refers to, through to its termination. In Common Lisp parlance, the run-time lifetime of a binding is called its "extent".
If a language supports lexical closures, then lexical variables never "go away" while it is possible for a closure to be invoked which refers to their values. This situation is called "indefinite extent".
"Dynamic scope" is an informal term for "indefinite scope, dynamic extent". See here: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node43.html [cltl2, 3 Scope and Extent]
This terminology lets us distinguish nuanced situations such as:
- C non-static local variables: lexical scope, dynamic extent (go away when block scope terminates).
- C static locals: lexical scope, indefinite extent
- Common Lisp blocks: lexical scope, dynamic extent
Dynamic binding is usually called 'fluids' in Scheme. There are constructs like fluid-let which create dynamic bindings.
In Common Lisp the normal LET creates dynamic bindings when the variable is declared to be special. Variables defined by DEFVAR or DEFVARAMETER are declared special by definition.
With dynamic scoping you also don't, because there is no lexical scope.
What am I misunderstanding?
> (defvar somevar 3)
> (defun printvar () (format t "somevar is ~D~%" somevar))
> (printvar)
somevar is 3
> (let ((somevar 5)) (printvar))
somevar is 5
> (printvar)
somevar is 3
If you only have global variables, you wouldn't get 3 back the third time, right?This made it click with me, finally. I can't yet put it into words nicely, but I do have an intuition for the concept now.