> One interesting bit that remains unappreciated about Kernel is that environments are reified and are "copy-on-write". So, if you make an environment change, it propagates forward but previous calls only see the previous environment unless explicitly give them access.

That's not quite how it works. Environments are mutable, but only the locals of an environment that we have a direct reference to. We cannot mutate any of the bindings in the parents without a reference to the parents, and the language provides no facility to obtain references to the parents given only a reference to the child.

We can however, explicitly pass around references to parent environments if we do have access to them, because environments are first-class, and we can move around references where we want. We can have a child mutate its grandparent environment, and those changes will be visible to the child's parent - but this is only possible if the child has a direct reference to the grandparent - which it would not have under normal circumstances - it has to be provided with that reference explicitly.

We could for example, bind our top-level scope into a symbol in the top-level, such that any descendants of the top level can mutate any bindings in the top level.

($define! *globals* (get-current-environment))

($define! x 0)

($define! print-x
($lambda () (write x)))

($define! foo
($lambda ()
($set! *globals* x 1)))

(foo)
(print-x)

Here we see that the lambda defined for `print-x` does not get an immutable copy of the top-level scope, because if that were the case, then the `x` printed by `print-x` would be a different `x` that foo mutates, and `print-x` would still print 0, but this should print 1.

Obviously, we would not do this in practice because it would let us rebind `print-x` and `foo`, or even rebind `*globals*` to some another value.

A safer way to do this would be to bind a setter for x in the top level.

($define! x 0)

($define! $set-x!
(($vau () top-level
($vau (value) #ignore
($set! top-level x value)))))

($define! print-x
($lambda () (write x)))

($define! foo
($lambda ()
($set-x! 1)))

(foo)
(print-x)

This would allow any descendants of the top-level to mutate `x`, but not mutate any other binding in the top level.

This works because the outermost `$vau` on $set-x!` is evaluated in the top level, where `$set-x!` is defined, so it receives the top level environment as its dynamic environment, which it binds to the symbol `top-level` in its local environment, and then returns an operative whose static environment is its own local environment.

When `$set-x!` is called, it ignores its caller's dynamic environment, but its static environment contains the symbol `top-level`, bound to the top level environment.

---

The immutable part of environments is the list of parents that any environment has. When we create an environment through say `(make-environment list-of-parents)`, the environment creates an immutable copy of the list of parents, which prevents whoever created the environment from being able to modify this list later. For example, we wouldn't want this

($define! parents (list some-environment))
($define! env (make-environment parents))
($set-car! parents some-other-environment)

The caller mutates `parents` after creating `env`, but this will not affect `env`, because it created an immutable copy of `parents`.

However, the creator of this list can mutate the bindings in the parent after creating the env.

($define! parents (list some-environment))
($define! env (make-environment parents))
($set! some-environment some-binding some-value)

This would be reflected in `env`, whose `some-binding` would now resolve to `some-value`.

But usually it will be the case that `some-environment` is encapsulated by the creator who will eventually return `env`, but no reference to `some-environment` - which will prevent any mutation of the bindings in the parents.

---

There is no way to mutate the ground environment because every standard-environment is a child of the ground, but with no direct reference to the ground. The ground is just its parent. The definition of `make-kernel-standard-environment` is interesting, because it's just

($define! make-kernel-standard-environment
($lambda () (get-current-environment)))

Because Kernel specifies that ground bindings are evaluated in the ground environment, and the lambda creates a child of the ground with no initial local bindings, then when we get that environment, we have an empty environment with ground as its parent.

Although Kernel is specified using mutable environments, the Kernel report does make them optional. It doesn't really go into depth about how one would implement Kernel with immutable environments besides a brief note.

If you do have immutable environments, then a fun challenge is how you would implement `make-kernel-standard-environment`. If using the definition above, then the environment returned by the call to `make-kernel-standard-environment` notably does not have the binding `make-kernel-standard-environment` in it (because the binding happens after).