> [!quote] [C2](https://wiki.c2.com/?ShallowBinding)
> In _dynamic_ scoping, by contrast, you search in the local function first, then you search in the function that _called_ the local function, then you search in the function that called _that_ function, and so on, up the call stack. "Dynamic" refers to _change,_ in that the call stack can be different every time a given function is called, and so the function might hit different variables depending on where it is called from.
At this point ([[2023]]); Lexical scope is so common and widespread that dynamic scope feels eerie. However, dynamic scoping - or the ideas behind it - are useful in many cases.
These may look different, but similar concepts are:
* Request Context
* [[#Thread-Local Storage]]
* [[Go]]'s `context.Context`
* Global Variable
* [[Keyword Arguments]]
* [[Odin]]'s context system - https://odin-lang.org/docs/faq/#context-system
* [[environment variable]] - most subprocess invocation preserves the envvars, forming a tree of envvars that follows the process tree.
# Problem of the *Context*
... ...
# Request Context
A typical server process handles multiple requests concurrently. For most applications, individual requests are independent of each other (unless you're writing a chat application, for example). In this case, we would like to "zoom in" the application so that we only focus on a single request-response flow.
This "zoomed in" application is very simple. In this world, it's simpler to think of certain states - request, response, TCP socket, etc... as a global variable (in this zoomed-in application).
We would like to call these *Request-scoped variables*. However, Request-scoped is an application defined term. At the [[Programming Languages]] level, no such support exist, although there may be building blocks to allow frameworks to implement this.
There are multiple ways to implement this.
## Global Variables
Above, I caveated wit "Imagine if you zoom into the application..." but this was the model of the [Common Gateway Interface (CGI)](https://en.wikipedia.org/wiki/Common_Gateway_Interface), especially PHP. A new process would be spawned per request. This process is naturally request scoped.
However, process spawning is not efficient. FastCGI allowed reuse of processes, but the fact that you can treat your application as a global variable doesn't change - though one needs to be careful about sharing states between executions.
## Side note; in defense of global variables
I would expand the above "zoom in" argument further and this is desirable. Conceptually, being able to interact with only a singleton instance of say, *an user* within a scope of question is simple and elegant.
Large class of one-off programming (say, Jupyter notebook) is done with this way. The problem of global variables is the problem of *software engineering* - it's the difficulty of productionizing and maintaining (large blast radius of access).
(Also, I'm starting to realize that global variables got a bad reputation in C due to the lack of namespacing. Global variables were both (A) singleton state within the application, and (B) pollute the global namespace).
# Context
The thing is, passing context around all the time is cognitively burdensome.
* [the term "context" in programming](https://stackoverflow.com/questions/6145091/the-term-context-in-programming)
Easiest way is to pass these around. There are two ways to do this:
## Pass them around individually
```python
def layerA(a, b, c):
...
return layerB(a, b, c)
def layerB(a, b, c):
...
return layerC(a, b, c)
def layerC(a, b, c):
...
```
This pattern is called [[Argument Drilling]] in React. This has the benefit of being clear , but to add an additional parameters deep in the layer, one must refactor all the intermediary layers.
- [x] TODO: Figure out what this pattern is called; The philosophy of software engineering. ✅ 2024-05-22
## `context` object
```python
requestContext = (a, b, c)
def layerA(reqContext):
...
return layerB(reqContext)
def layerB(reqContext):
...
return layerB(reqContext)
def layerC(reqContext):
...
```
This pattern has the opposite problem; it's not clear what's part of the `requestContext`. It's easy to add "one more field" into this type; it's hard to understand what all these fields are used for, thus it will accumulate fields over time.
# Thread-Local Storage
[wiki](https://en.wikipedia.org/wiki/Thread-local_storage)
Thread local storage is a clever solution / hack to this problem in many programming languages. Threadlocal provides per-thread global variables.
*Request-scoped variables* can be implemented in this manner; in threaded servers, a request-response lifecycle is owned by a single thread. Thus, thread local scope effectively becomes the request scope.
There are many caveats to this; For example, what if the entire request-response lifecycle is not executed by a single thread? what if some portion of the work is done by another thread, asynchronously? There's also the risk of thread reuse and data sharing between multiple requests, as many servers use thread pools.
Note that threadlocal isn't just a global `map<ThreadID, T>` but something that is supported by the operating system (PThread API). I'm not sure what happens behind the scene though.
## [[Go - context.Context]]
* Go's solution.
* Many other libraries (like [[Temporal]]) took an inspiration from it, providing their own context object. Custom context is sensical in a hermetic environment (where timeouts and cancellation propagation may not be relevant).
# [[Asynchronous Programming]]
## In [[JavaScript]]
Node has [AsyncLocalStorage](https://nodejs.org/api/async_context.html) - https://twitter.com/leeerob/status/1771976821569093642
Web has [Angular's zone.js](https://github.com/angular/angular/tree/main/packages/zone.js), which requires monkeypatching.
Dart's [zone](https://api.dart.dev/stable/2.19.3/dart-async/Zone-class.html)
[React Hook](https://react.dev/reference/react)
## In [[Python]]
[[contextvars]]
# Related
## Arena Allocator
[Region-based memory management](https://en.wikipedia.org/wiki/Region-based_memory_management)
Arena allocators simplify the memory management by exploiting the semantic of the code. Using a typical server with a request handler model, a single request-serving lifecycle can be considered a "scope". #todo
## [[React Hooks]]
one can argue that this is an example of *dynamic scoping* in example; as the hooks are evaluated in its own context. That context is not explicitly passed off;
## Exception Handlers
If one squint their eyes, exception handlers have the dynamic scoping behavior as the stack is traversed until the handler is found.
## Articles
- https://medium.com/source-and-buggy/context-the-missing-feature-of-programming-languages-7c1095fe8d32