Heh, that's an interesting question; I don't know of any. I think Haskell's State monad basically puts you into a mutable value semantics programming model (@Alvae, please correct me if I'm wrong), so if you used that stuff a lot, you might get an appreciation for it. But I just did a quick search for examples and I'm afraid I can't really recommend the literature I'm finding. It's mostly obscured by jumping through the hoops implied by a language where you can somehow construe your mutation as actually non-mutating. I'm collaborating on a book with Sean Parent, and we're certainly going to address it, but that's in the future…
It's quite natural to want to pass a Node around, particularly to a view. But any mutation of that Node is pointless because you're just mutating the local copy.
I don't understand why you'd say that. If you're going to pass the node around for mutation, you pass it inout (or equivalently, as the self of a mutating method), and then the mutation isn't lost. The tree you defined at the top of the message is a perfectly good value-semantic tree (well, forest actually, since you have multiple roots). So this mutation will work:
extension Node {
mutating func expandAll() {
isExpanded = true
for c in children { c.expandAll() }
}
}
extension Tree {
mutating func expandAll() {
for n in rootNodes { n.expandAll() }
}
}
Thus, anywhere in the code that needs to mutate a Node must have a way of accessing the component that owns the Tree and having it update the Node .
But how does the Tree know which Node needs updating? Inevitably you have to provide an id on every type, which in Swift sample code manifests itself as UUIDs all over the place.
Now that sounds like something that does actually happen… but misstated, at least if you're referring to what I think. What I'm thinking of is not about mutation at all. If you want to be able to traverse an arbitrary graph, the functions doing the traversal do need to have access to the whole graph, and there needs to be some representation of node identity. It doesn't have to be a UUID, though.
[Your tree doesn't have this problem because value whole-part relationships always have the shape of a tree, and you've used aggregation of parts to represent the parent-child relationship.]
The simplest representation of an arbitrary graph (just the relationships) is probably [[Int]], where each vertex is identified by an index in the outer array, and the inner arrays represent the destinations of outgoing edges on the corresponding vertex.
If you want to traverse this graph, you can never let go of the whole graph and just handle "a vertex." And that requires a shift in thinking if you're used to thinking of a network of objects. If a function needs to access a thing x, that thing either needs to be passed to the function (either by value or inout) or the function needs access to the thing y that x is a part of, and some directions to get to x in y. In this case, those directions are just an Int, but in general I guess they could be as complicated as an arbitrary KeyPath.
And when the Tree does mutate a Node , perhaps by toggling the isExpanded property, then there's chance of that triggering a potentially large copy-on-write operation.
There's no risk of that with the first Tree you defined unless you're storing copies of it.
Just one example where a reference type implementation is, in my eyes, relatively straigth forward, but a value type implementation is not. The benefits of value types are obvious to me, but their implementation has proven a challenge at times.
I'm afraid I don't understand why the value type implementation—of a tree, specifically—poses any problem at all, because of the whole-part tree shape mentioned above.
Would love to know how to improve designs like this and if there are any resources I can refer to for guidance.
FWIW, I have watched a lot of the C++ talks on YouTube that surface if you search for value types or value semantics.
Well, maybe watching Sean's talk on relationships would be helpful. It sounds like maybe what you're really struggling is how to represent relationships that are not a whole-part relationship.
(In hindsight, perhaps this comment should have been its own post. Happy to move it over if necessary.)
Sorry, my fault for bringing in the question of how to teach value semantics. If you want to start a new thread on that topic I'd be happy to go there.