Earlier quoted context omitted.
The problem with local variables is that you can then not call a function that uses them unless you define such variables. One shouldn't be forced to know of such variables before calling such a function, since then they'd hardly be different from regular arguments. The difference with statically-scoped global variables is that you can't set them to different things in different threads, and you have to be careful to…
If I ever saw anyone I worked with trying to do something like "setting a global variable to different things in different threads", I would have very strong words for them. If they didn't listen, I'd have even stronger words for their manager: "Fire this bozo immediately". In what sane world is that considered a good idea? Is there any way in which doing so via "dynamic scoping" makes it any less of a horrible idea?…
Dynamic Scoping in C++
21–30 of 64 posts
Re: Dynamic Scoping in C++
#22I never understood the advantages of dynamic scoping. It always seems to just boil down to a worse global or thread-local variable. Is there a simple real-world example that would explain when dynamic scoping would be better than some kind of access protocol to a shared value?
One pattern that most languages don't support encapsulating is this: Say you have a() which calls b() which calls c() which calls d(). d() needs to get some data from a(). The typical way to handle that is by passing that data as parameters through b() and c(), but that couples those middle-level functions to a() and d(). Any time you change the data a() needs to get to d(), you have to touch b() and c() too.
You could wrap the data in some opaque "context" parameter and pass that through b() and c(). That's an OK solution and is pretty common. But a() still has to opt in to that pattern, which means b() and c() are still coupled to the choice to use any encapsulation at all.
Dynamic scoping is a solution to this. a() can bind a value to a dynamic variable and d() can access it without it having to pass through b() and c() at all. It essentially gives you a side channel for parameters.
A more concrete example is trees of UI components. Pretty often you have some big top level UI component that has a lot of application-level business state. Down in the leaves, you have UI components specific to that application that need that state and render it. But in between those you have a bunch of generic UI components like list views, frames, tabs views, radio button groups, that have nothing to do with your app and just visually arrange the UI.
You really don't want to make a new frame widget class every time you need to pass a bit of business data through it into the thing inside the frame. So instead, what a lot of UI frameworks do is support dependency injection. A widget at the root of the tree can provide an object of some type, which makes it implicitly available to all child widgets (transivitely) of that widget. Children far down in the tree can request the object without widgets along that having to pass it along explicitly.
Dependency injection is essentially a re-invention of dynamic scoping.
Re: Dynamic Scoping in C++
#23This isn't dynamic scoping; this is just a global variable with a stack of values. I appreciate the syntax is sort of the same (though the * makes it different in a very important way) but the meaning isn't. You should at least implement this with thread local storage, though, if you are going to do this.
This isn't a latte; this is just an espresso with steamed milk added to it. :)
What you describe basically is how dynamic scoping is mechanically implemented under the hood.
Re: Dynamic Scoping in C++
#24Earlier quoted context omitted.
If I ever saw anyone I worked with trying to do something like "setting a global variable to different things in different threads", I would have very strong words for them. If they didn't listen, I'd have even stronger words for their manager: "Fire this bozo immediately". In what sane world is that considered a good idea? Is there any way in which doing so via "dynamic scoping" makes it any less of a horrible idea?…
It is a bit sad that Lisp isn't taught universally anymore. It might not be the most popular language, but it offers a lot of concepts which are worthwhile studying. Dynamically-scoped variables (more precisely: variables with global scope and dynamic extent) are one such wonderful thing. In languages which offer those, one might use one in a function to hold a state which isn't related to the primary task of that fu…
Re: Dynamic Scoping in C++
#25Earlier quoted context omitted.
I think best practice among languages that support dynamic scoping is to only make use of it for global variables. As I understand it, one should only read or shadow, not modify, these variables. Since that's the case, besides the thread issue you mentioned, I'm not sure this solution is lacking. I don't know much C++, though, so I might be missing something.
If you have multiple threads which try to bind the dynamically scoped variable to a new value, that should work fine and result in different values for the variable in each thread. In this implementation, the threads will corrupt the data structure and result in undefined behavior.
That would work with proper threadlocals. However with dynamic variables you'd also get this for different sub-thread stacks. Not all languages can expose that but languages with generators do e.g. in Python if you `yield` then the stack is suspended, this means a threadlocal you've changed would not be rolled back, but a dynamic variable (which Python does not have) would.
Re: Dynamic Scoping in C++
#26Earlier quoted context omitted.
> I don’t see how this is functionally different to passing an object that contains references to the relate to variables; which I’ll call a context object. It's the same difference as using environment variables vs command line arguments. Imagine all programs having to pass TERM, DISPLAY, HOME, etc. as arguments in case some descendant process wants to use it and the user override. Like passing TERM, DISPLAY to git…
This is the best argument I've read for dynamic scoping thus far.
If you're in CL however, you can just rebind standard-output and you'll capture what anyone downstack sends there.
Re: Dynamic Scoping in C++
#27Earlier quoted context omitted.
> I don’t see how this is functionally different to passing an object that contains references to the relate to variables; which I’ll call a context object. It's the same difference as using environment variables vs command line arguments. Imagine all programs having to pass TERM, DISPLAY, HOME, etc. as arguments in case some descendant process wants to use it and the user override. Like passing TERM, DISPLAY to git…
This is the best argument I've read for dynamic scoping thus far.
For me it seems like dynamic scoping is very similar or at least related to dependency injection. Which seems to be one of those things that really have polarized opinions of developers. Some people love it because the code looks really neat and it is really easy to add new pieces. Other hates it because it is really hard to understand what is happening under the hood.
I would say that dynamic scoping is a bit worse (in my opinion) in that it is like dependency injection that are supposed to be modified one or more times in between so it will be almost impossible to figure out why a value is what it is when it goes wrong.
Re: Dynamic Scoping in C++
#28I never understood the advantages of dynamic scoping. It always seems to just boil down to a worse global or thread-local variable. Is there a simple real-world example that would explain when dynamic scoping would be better than some kind of access protocol to a shared value?
One of the key tenets of software engineering is encapsulation : minimizing the number of parts of the program that need to care about X for any given X. Languages have lots of different ways to encapsulate different kinds of stuff from different kinds of code. Local variables encapsulate variables from other functions. Interfaces encapsulate method bodies from invocations. OOP encapsulates state from operations that…
Re: Dynamic Scoping in C++
#29Dynamic scoping moves a bunch of correctness checks from compile time to runtime. It basically introduces all the problems that come with shared mutable state across different functions/methods. It becomes hard to reason about who mutates state where/when.
Ergo, dynamically-scoped variables are shadowable side-channels that can influence the behavior of a function
And thought that is exactly why not to use dynamic scoping. It makes every function impure by default.Re: Dynamic Scoping in C++
#30I never understood the advantages of dynamic scoping. It always seems to just boil down to a worse global or thread-local variable. Is there a simple real-world example that would explain when dynamic scoping would be better than some kind of access protocol to a shared value?
Safely intercepting global IO.
The average language is never going to thread IO explicitly, so if your callee has not added explicit hook points then all you can do is try to swap out the relevant subsystem, but your average standard IO is usually not even thread-local, and when it is that doesn't help when the language has sub-tread stack swapping (e.g. Python's generator will just suspend the stack relevant section of the stack entirely so if you've updated a threadlocal in a coroutine it is not rolled back on suspension). Plus you still need to remember to properly clean up your threadlocals as they won't self-revert.
With dynamic variables you can just rebind the variable. Only stack frames following yours will see the update, other stacks will be unmolested and none the wiser regardless of your shenanigans.