|
Because Passerine is pass-by-value, it only allows each reference to be owned by one object. > So if some code somewhere hands me a list that contains references to some heap data, how do you track this? Passerine is a high-level language, so it does not give the programmer control over whether something is a list of references to data on the heap or a list of values. Assuming `a`, `b`, and `c` have been defined, If I write this: list = [a, b, c]
`a`, `b`, and `c` will be moved into the list upon construction. If I do something like: list = [a, b, c]
print (foo a c)
`b` will be moved into the list, and copies of `a` and `c` will be moved into the list.So, to now answer your question: list = [a, b, c]
list = drop_first list
> We lost/dropped/gave up a. How do we know how a was tied to the stack?`a` was tied to the stack because it's in `[a, b, c]`, which is tied to the stack via `list`. > How many other references might point at the object previously referenced by a? Can we collect that object now? By definition, none - `a` is a unique value. Now that we dropped `a`, there are guaranteed to be no other references of `a`, so we can collect that object now. > Is this the case where reference counting kicks in? No, the reference counting is a small optimization for sharing immutable values in closures. If a closure captures a value, say a large list, we don't want to go around copying this willy-nilly: big_list = [...]
c = () -> {
-- big list is copied when the closure is made
-- this happens because the reference in the closure can outlive local stack frame
a -> first big_list
}
-- these calls to c do not copy big_list again
a_outer = c ()
a_another = c ()
Closures need to copy values immutably to prevent the construction of cycles. I wrote a long explanation on why this is the case, but I've decided to move it to the blog post when I publish it. I thought of a way to efficiently count references acyclically, but I need to work it out before I made any more claims.Hope this helps! |