I’m not familiar with the theoretical aspects, but what you’re describing reminds me of Tcl: arguments to procedures can be passed unevaluated (quoted, using Tcl terms) and the procedure itself can alter the caller’s environment via uplevel.
“I regret this decision, and a thousand curses on anyone who chooses to use this in a code base I have to work on”
I’ve been at the receiving end of this particular “feature” and it was awful. Your logic is splayed all over the place, and you’ll often get mystery errors, because someone else’s code makes some undocumented, un-typed assumption about the availability of some magically named variables. This is not a feature, this is bugs and misery in disguise.
R is even more wild because you can access the entire definition of a function from the function object itself. You can literally make a new function that's a copy of an existing function with some lines of code injected.
In Kernel, the environments are completely reified--they exist as a thing and you can pass them around.
However, if you don't get an environment passed in, you can't get access to it. In addition, the environments in many ways are append-only, you can change what you see as "car" but you can't change what previous callers see as "car" unless you get a handle to their environment but that won't change previous calls.
It's really quite tricky. It's doubly tricky because there are some environment mutations that you need to do as the system that you need to prevent as the user once everything is bootstrapped.
I really wish Shutt had implemented a version in C/C++/Ada/anything systems language and not just a metacircular scheme implementation. The metacircularity obscures the management of the environments that is key to the implementation. It also obscures just how much garbage Kernel cons-es up in order to maintain all of the necessary housekeeping to pass all this stuff around.
Alas, he is no longer with us to request anything of. RIP.