I might be interpreting the design incorrectly, but I’m not a fan of inlining or coupling mutable types like this. You already hinted at it with object identity and comparing with the layout of immutables inlined in mutables, but the primary characteristic of mutable types is multiple references “observing” a mutation. Because some of the references don’t outlive local scopes or some objects with references can be freed, a mutable instance generally must be allocated independently on the heap instead of the stack or inlined to another object.
By coupling objects like this, you’re sacrificing the free-ability around said objects with references. With the inline
proposal, you’re making w
live as long as p
does, even if you only need p
. Because of Part
’s object identity, you can’t extract the same instance from the Whole
. In the examples of Whole
containing little besides Part
, that’s not a big deal, but if Whole
contains a lot more data like a huge array, you could be using up a lot of memory just for a small Part
. This is similar to a common gotcha with views of arrays; while it’s faster and sometimes only correct to make a view, a view instead of a copy keeps the whole array alive, even if you only need a small fraction of its elements. Along with other performance reasons, indexing Array
s thus defaults to copying instead of views, and we must view
with care.
vtjnash’s proposal is one version of a generally accepted view that mutables are actually kind of overrated. When we need (or it’s convenient) to have multiple references that observe a mutation, mutable types are appropriate and useful. But if we only need to update data in one place, reassignment of a different immutable instance to one reference works just fine e.g. count+=1
. Accessors provides the syntax for doing this with changes to nested fields or elements, but as you said, it hurts performance to go through constructors. While possible for Accessors to skip constructors to new
instantiation, we’re still relying on the compiler to optimize it to a simple field update. In the extreme case where we can instruct Julia to update fields to accomplish semantic instantiation and reassignment, we wouldn’t even need named mutable types sometimes, we could just reassign things to a Ref
instance.