Janet has dynamic scoping in the form of "dyns" where you can put variables on the function stack ("fibers".) Then you look for them with (dyn <name>) and it does a stack walk to find whatever value has most recently been bound to that name. I've found with some light tinkering that this kind of does away with dependency injection shenanigans, since you can put factories on to fiber slots and have objects just reach in there when they need it. No more special purpose registries and the like. They use it for some clever things (like having certain print functions default to stdout, but you can re-bind the output target via scoping). Obviously, being an interpreted VM language makes this a lot easier though.
I've tinkered with doing something similar in Nim. In this case it involves global (well, thread-local) variables which point to the most recent binding. Then use of a macro to insert push (and defer: pop) statements to those. In theory that would create a similar API without needing the special stack walks.
There is something of an art to deciding what values should be subject to dynamic scoping though. In Emacs its "everything" which can be too woozy. It is somewhat interesting to tinker with though since its also what gives Emacs scripts a lot of power. "For the purpose of this entire stanza, assume the maximum width of text allowed is ..."
Curious what people think of this way to approach it.