| These examples don't really do dynamic scoping justice, and there are a few inaccuracies here. > A dynamically scoped variable can propagate downward, upward, or sideways (ie sibling methods in the call graph can access the values that you placed into the dynamically scoped variable). The binding only propagates downwards, never sideways or upwards. You can send values through bindings upwards, downwards, or sideways, but all you're doing is assigning a value to a memory location. This isn't particularly novel, because you can do the same thing with any other type of variable, or with fields in a structure, etc. > A dynamically scoped variable is basically just a global variable that isn't quite 100% global. So you can get into all the same problems that a global can get you into with the added benefit (or sometimes problem) that any two instances of a dynamically scoped variable might actually be different because they are coming from a different caller. This is no more a problem than it's a problem that when you call f(x), you might get a different value for x each time you call it, because x comes from the caller. The common use cases for dynamic variables are certain seldom-changed parameters for functions and maintaining context. The basic reason for using them is to avoid having to pass a potentially large set of variables through a potentially large number of intermediate functions. Dynamic variables are most similar to thread-local variables. For seldom-changed parameters, consider standard-output (asterisks and HN don't mix). It's common to want to redirect the output of some block of code to a different location, in a shell script you could do this very simply: {
echo a
echo b
} >file.txt
In Lisp, you can do this by binding the standard-output variable: (with-open-file (stream "file.txt" :direction :output)
(let ((*standard-output* stream))
(print 'a)
(print 'b)))
You can see that this isn't really some mysterious or dangerous phenomenon, it's a way to pass things down to functions without having to pass them as arguments. It's also commonly used for tracking the context of things like requests in a web server. In Haskell you'd do this by using a reader monad, and in Go you'd do this by adding entries a dictionary attached to your context.Context object. |