> What's a "copy-on-write reference"?
A copy on write reference (CoW) is a pointer to some immutable data, that when written to, is copied to a new memory location first.
> Does this mean that if write a function that takes a million-element list, and inside the function I mutate the last element, the entire list will be copied (inside the function, but invisibly to the outside)?
Only if you use the original list later:
big_list = [ ... ]
new_big_list = mutate_last big_list
print big_list
This would copy `big_list` because we use it in `print` later. If you do not use big_list later: big_list = [ ... ]
new_big_list = mutate_last big_list
Or reassign the variable: big_list = [ ... ]
big_list = mutate_last big_list
the original list is not copied, because this is the last usage of a value, and last usages pass the actual value rather than CoW, allowing the program to mutate it directly. This is what Functional but in Place (FbiP) means - You can write code in a functional style, but when executed it will mutate data in place.> Can I even mutate lists or anything else? I don't see any clear information about whether Passerine has mutable data at all.
So, this is less about Vaporization and more about Passerine. Passerine does not have mutable data, only mutable references to data*. So the reference can change (or like in a record, the reference in a field can change), but not the data itself. Because of FbiP, however, mutations are optimized to an actual mutation rather than a copy and an overwrite.
* This is needed if one wishes to extend HM type inference to support mutation, IIRC.
> Does this mean that any non-last usage is a copy?
Any non-last usage is a potential copy. If the function you pass it to does not mutate the data, no copy will be made. Last references also include reassignments, so something like:
big_list = [ ... ]
for x in 0..100 {
big_list = big_list.append x
}
Does not make 100 copies of `big_list`.> Again, are these eager copies or lazy copy-on-writes?
These are eager copies, and immutablility is enforced by the language itself. This tradeoff has to be made, because mutable references allow for the construction of cycles. These references are reference counted if a copy of the closure is made or another closure closes over the same value in the same scope. It's safe to do this because these references are immutable, and when all closures referencing them go out of scope, they will be dropped.
> How are non-closure references garbage collected?
Non-closure references are 'garbage collected' when they go out of scope. Vaporization basically ties everything to the stack, and prevents values stored on the heap from becoming dissociated with it.
> I would be interested in a much more detailed write-up of this memory management technique.
Thanks for the interest! There's still a bit more formal verification to do, which is why I left this broad claim to the FAQ - we're rewriting the website, so the mention of it there is a bit outdated.
Given that it was just asked here, it looks like I need to extend the FAQ some more, given how frequent of a question it is. ;P
Have a nice day!