Live data from Hacker News

Dynamic Scoping in C++

blog.dokucode.de

31–40 of 64 posts

Re: Dynamic Scoping in C++

#31
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. 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.

> What you describe basically is how dynamic scoping is mechanically implemented under the hood.

It's certainly how that's commonly emulated but that can leak out e.g. CPython uses threadlocals for decimal contexts, but if you set a localcontext in a coroutine / generator and suspend that, the information leaks out.

I assume the same happens with gevent unless you `patch_thread()`, and even then that assumes `decimal` always deref's threadlocals from the python-level module rather than statically resolve them.

Re: Dynamic Scoping in C++

#32
post #24

Earlier quoted context omitted.

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…

just to be sure, can't this problem be handled in OOP with a "trivial" class (or object) field?

Technically it's possible but AFAIK people don't normally extend e.g. every business object with a logger field or an output stream? In fact that's usually a very bad pattern, something akin to a "god object".

Re: Dynamic Scoping in C++

#33

Earlier 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?…

Part of this is Lisp-only history, outside of old Lisp users dynamic scoping really has few friends although the somewhat similar environment variable mechanism is widespread and accepted: https://en.wikipedia.org/wiki/Scope_(computer_science)#Histo...

Yeah, “12 factor apps” basically use dynamic scope for dependency injection. But, also, I believe the Reader typeclass in Haskell is basically equivalent to dynamic scope? Haskellers seem to use this and a lot of similar techniques (Free monads/mtl style) to work around the lack of dynamic scope :).

Re: Dynamic Scoping in C++

#34
post #13

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

Think about passing some ambient context around. In Go, you do it by sticking an additional "ctx context.Context" parameter in every single function that conceivably may use it directly or indirectly, i.e. may call some other function that may or may not use it.

And sometimes you need to set up a derived context for one function call, but continue to use the previous one in other calls:

    func Foo(ctx context.Context, ...) {
        Bar1(ctx, ...)
        derived := makeDerivedContext(ctx)
        Bar2(derived, ...)
        Bar3(ctx, ...)
        // ...
    }
It's not hard, but just extremely tedious, esp. if context is mostly utilized in the leaf functions, and the most of the rest of your code simply passes it around.

That's where dynamic scoping enters in.

Re: Dynamic Scoping in C++

#35
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.

IMO, the most comprehensive solution to this mechanism is provided in Racket scheme's parameterize system. Racket's parameters are about as safe as global variables can get. https://docs.racket-lang.org/guide/parameterize.html

What value does a forked thread get? The value at the dynamic scope of the parent at the point of thread creation.

What happens if a delimited continuation is invoked by a different thread compared to the one that created the continuation? If a parameterize call was made within the continuation's delimited extent, then it moves with the continuation. If not, it'll be in the executing thread. In either case the answer is consistent: The value within the dynamic extent of the continuation is used.

What happens to other threads if one overrides a parameter within its own dynamic scope? Nothing, threads don't have a dynamic scope relationship between them after thread creation.

Re: Dynamic Scoping in C++

#36

Earlier quoted context omitted.

This is the best argument I've read for dynamic scoping thus far.

Incidentally that makes it super easy to mock out or intercept subsystems if they use dynamically scoped hooks e.g. in most languages intercepting stdio is a pain in the ass (you might have to swap out multiple globals, it's not thread-safe, etc…). If you're in CL however, you can just rebind standard-output and you'll capture what anyone downstack sends there.

Or rebind standard-input and feed an interactive program input from a file or other stream.

Re: Dynamic Scoping in C++

#37
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 fighting off global states has been a worthy goals of languages, libraries and framework. Without it, you can say goodbye to reproducible behavior and multi-threading.

(If dynamic scoping is thread-local, you still have the issue that anything can affect anything else, so nothing can be assumed to be reentrant anymore.)

Re: Dynamic Scoping in C++

#38
I implemented exactly the same thing 20 years ago. It looked something like:

  Dynamic foo;  // define at global scope

  {
     DynamicBind foo; // re-bind dynamically
  }
It used thread-local storage and all. The global constructor for the Dynamic template class would allocate the thread specific key. The DynamicBind template class did the saving, location altering, and restoring.

Re: Dynamic Scoping in C++

#39

Earlier quoted context omitted.

Part of this is Lisp-only history, outside of old Lisp users dynamic scoping really has few friends although the somewhat similar environment variable mechanism is widespread and accepted: https://en.wikipedia.org/wiki/Scope_(computer_science)#Histo...

Yeah, “12 factor apps” basically use dynamic scope for dependency injection. But, also, I believe the Reader typeclass in Haskell is basically equivalent to dynamic scope? Haskellers seem to use this and a lot of similar techniques (Free monads/mtl style) to work around the lack of dynamic scope :).

Reader is similar, but I wouldn't say equivalent, because only those things working in the Reader can use that environment. For example, when you have project A using project B using project C, you can't define a putStrLn such that project C can use it and be able to override its output stream from project A, while keeping B completely ignorant of that ability of putStrLn or that C even uses putStrLn.

Reader is like a middle-ground between dynamic-scoping and explicit passing of a context argument around as you would in a statically-scoped language.

Re: Dynamic Scoping in C++

#40
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. 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.

Description !== Implementation
Post reply on HN