> Thanks to Skip's tracking of side effects the garbage collector only has to scan memory reachable from the root of a computation But any typical copying GC only has to scan memory reachable from the root(s) of a mutator. I suspect there are more interesting optimisations, but I am unable to discern them from this statement.
collect { // Some code return ...; }
Well, if you don't have a type-system that tells you what is mutable, you have to either scan all the mutable roots, or maintain a write-barrier to know what could have captured data in the scope.
The problem with write-barriers, which is the go-to solution for generational GCs. Is that it is a conservative approach. Meaning, it will promote garbage in the cases where the pointer that lived outside of the scope is dead.
Let's take an example, again, in pseudo code:
myObject = { field1: []};
collect { myObject.field1 = [A, B, C]; myObject = null; }
If you use a write-barrier, what is going to happen is that [A, B, C] is going to be promoted, because the barrier is going to track the object myObject, and it doesn't realize that it's dead.
However, instead, imagine you have a type-system that tells you that in that scope, the only thing that can be mutated is myObject!
Well, now you can run that in a loop, without accumulating garbage!
Makes sense?