Let's define a tree T:
A
/ \
B C
/ \
D E
Let's say a new node is added to E, yielding T':
A
/ \
B C
/ \
D E
\
G
In a mutable language this is an easy task - just update E's children and we're done. However, in an immutable world it is necessary to first know the path to E, then derive E' from E + new child, then derive B' and then finally A' ( = T').
This is cumbersome; ideally, there would be some function that would take the values of E and G (and possibly T) and produce T', without supplying the path to E.
I see two possible ways to attack this problem:
- parent references - this way, every node would be able to derive its path to the root. Two problems: creating two mutually referencing nodes (i.e. parent <-> child) is a problem in a purely functional language (any easy solutions?); and whenever E -> E' is derived, all of E''s children need to be newly derived as well, since they now store the old value of E instead of E'.
- zippers - every node stores a zipper on creation, derived from its parent zipper. The mutually referencing problem disappears, but still, when E -> E' is derived, all of E''s childrens' zippers need to be derived as well, since they now point to the old tree E.
Is what I desire even possible with a reasonable performance in mind? Many thanks for any input!
Another Option, based around doing a Lazy Replace. If it is performance critical and will have alot of changes made to it, I would suggest benchmarking it.
I have implemented it in F#, however I don't think i've used anything "inpure" except for my printing function.
This is a bit of a wall of text, The basic principle is to keep the tree Lazy, replace the nodes, by replacing the function that returns the node.
The trick is you need some way to identify an node, that isn't it's own reference/name, and isn't by value. The identification has to be duplicable onto the ReplacementNodes In this case I have used System.Object's as they are referentially each distinct.
Test Case/Example:
We saw at the end of the test that using an old node Name (/reference) for the object being replaced does not work. There is the option of creating a new type that has the reference Id of another node:
You could also edit the
ReplacementNode
definition, so that it preserves the ID, of the node it replaces. (by not just returningnewNode
, instead returning yet another new node that has thevalue
, andgetChildren
of thenewNode
, but theoriginalRefId
of thenodetoReplace
)