Live data from Hacker News

Dynamic Scoping in C++

blog.dokucode.de

41–50 of 64 posts

Re: Dynamic Scoping in C++

#41
post #2

This 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 dynamic scoping; this is just a global variable with a stack of values.

I don't know what you think dynamic scoping is? Because 'global variable with a stack of values' is what it is.

Re: Dynamic Scoping in C++

#42

So, dynamic scoping are global variables... except with even way way way worse unpredictable behavior. Any function from three-level remote libraries can invisibly modify the meaning of code. Sure, it allows for neat tricks, I suppose. It mostly allows impossible to diagnose error conditions since what happened actually depends on anything that may have happened before, invisibly. I find it particularly amusing since…

[deleted]

Re: Dynamic Scoping in C++

#43
post #2

This 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 dynamic scoping; this is just a global variable with a stack of values. I don't know what you think dynamic scoping is? Because 'global variable with a stack of values' is what it is.

Some implementations search up the stack for a binding. Which is slower, but works correctly with multithreading.

Re: Dynamic Scoping in C++

#44
post #43

Earlier quoted context omitted.

> This isn't dynamic scoping; this is just a global variable with a stack of values. I don't know what you think dynamic scoping is? Because 'global variable with a stack of values' is what it is.

Some implementations search up the stack for a binding. Which is slower, but works correctly with multithreading.

I'm not sure what you think is the important difference between these implementations?

'Dynamic binding' is an abstract concept. The abstract concept is a global variable with a stack of values. How you implement it and whether you share the call-stack instead of a separate stack is is up to you. It's still the same dynamic scoping.

Re: Dynamic Scoping in C++

#45
post #2

This 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.

Thread awareness is not a required part of the description of dynamic scoping.

Thread local storage does not make it absolutely re-entrant.

We could move the True Scotsman goalposts even farther out and say that we appreciate that the syntax being fine, but your approach doesn't work with interrupt handlers.

Re: Dynamic Scoping in C++

#46
post #43

Earlier quoted context omitted.

> This isn't dynamic scoping; this is just a global variable with a stack of values. I don't know what you think dynamic scoping is? Because 'global variable with a stack of values' is what it is.

Some implementations search up the stack for a binding. Which is slower, but works correctly with multithreading.

The deep binding approach will not work correctly with multithreading any more correctly than shallow binding, if there is only one such stack, or any shared state whatsoever that is not atomically manipulated.

It will work if there is a spaghetti stack. So that is to say, each thread extends the dynamic environment with a newly allocated frame that points upward to the parent environment.

Each thread needs a thread-specific pointer to the top of its own dynamic environment chain.

Re: Dynamic Scoping in C++

#47

I 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?

It's a stack-local global variable.

Here's one case where I wished I had it. I was writing a tool (in Python) for some scientific task. Parse a file, do some calculations, call out to some library to do more calculations. The whole thing was pretty complex, but cleanly architectured. But then the requirements changed. I had to do something different in the innermost function, depending on the configuration. This was probably 6 layers of functions deep.

Now I had two options: add another parameter to every function to carry my configuration variable, or put everything in a class and use a member field. I couldn't use global variables, since I was doing many of these calculations concurrently. And I didn't want to add new parameters, since it clutters the code, and it mixes different levels of abstraction. Most of the intermediate functions don't care about what's going on at the lowest level. Yes it changes their output, so from strictly "functional" best practices I should string along a parameter. But it felt wrong anyway. So what I did was cram everything in a class and call it a day.

With dynamic scoping, I could have put the configuration in a dynamically scoped variable.

Ideally, there would be a way to specify that a function takes dynamic scope. Then tooling would understand that all the intermediate functions have a controlled amount of impurity. In pseudocode:

    MyResult myCalculation(float mass, float energy) (dynamic string extratext) {
        // do the calculation and add extra text to the result
    }

    // way up the call stack:
    using dynamic extratext = "Preliminary, do not publish" {
        calculateAllTheThings();
    }
    
I know this goes against the current trends (make functions pure if possible, avoid mutable state, think a certain way about data flow...). But in practice, those trends sometimes work fine, and sometimes produce convoluted code. In some cases, I find it easier to produce code that looks clean and functional from a domain logic POV, and add stuff that is orthogonal to it (logging, presentation, ...) via a different mechanism.

Re: Dynamic Scoping in C++

#48

Earlier quoted context omitted.

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…

Ok, that makes perfect sense. To me dynamic scoping was very specifically a language mechanism. What you're describing is more of a framework mechanism and something that would be easier to make easy to understand.

Right, UI frameworks like Angular do dependency injection at the framework level because the underlying language doesn't have direct support for dynamic scope.

Sort of like how you can do object-oriented programming in C by making a struct of function pointers to create your own v-tables.

Re: Dynamic Scoping in C++

#49
post #43

Earlier quoted context omitted.

Some implementations search up the stack for a binding. Which is slower, but works correctly with multithreading.

The deep binding approach will not work correctly with multithreading any more correctly than shallow binding, if there is only one such stack, or any shared state whatsoever that is not atomically manipulated. It will work if there is a spaghetti stack. So that is to say, each thread extends the dynamic environment with a newly allocated frame that points upward to the parent environment. Each thread needs a thread-…

What do you mean by a multithreaded runtime with only one stack?

Re: Dynamic Scoping in C++

#50
post #43

Earlier quoted context omitted.

Some implementations search up the stack for a binding. Which is slower, but works correctly with multithreading.

I'm not sure what you think is the important difference between these implementations? 'Dynamic binding' is an abstract concept. The abstract concept is a global variable with a stack of values. How you implement it and whether you share the call-stack instead of a separate stack is is up to you. It's still the same dynamic scoping.

Only one of them works with multiple threads. Dynamic scoping based on save-and-restore of symbol value slots is completely incompatible with multithreading. That's why Emacs still doesn't have threads.
Post reply on HN