Description
Problem/Opportunity
Movement functions return nil
upon reaching the end of the movement. This possibility requires to always break the flow of manipulations with checks since for code that makes changes it is impossible to keep the accumulated changes.
Proposed Solution
Instead of returning nil
, we could introduce a concept of a zipper that doesn't have a current node, but still has a current position, i. e. it is still within a given form (i. e. parents are the same) and there are still siblings to the left and/or to the right. Only the current node portion of zipper is nil, but everything else isn't. So a zipper may point at location between nodes, or before the first node, or after the last node. It also keeps all the accumulated changes. Many functions have a natural way of operation in such situation. For example, most navigation functions should work just fine. find
functions should work, and so on.
It may even be possible to simplify the API for other things. For example, remove
function may keep the position in tact - removal of a node means that returned zipper is pointing between the nodes and it is up to user to choose where to go from there. Insert
node function could simply insert node in the current position.
Similarly, left
and right
functions would be stopping upon reaching the position before the first and after the last element.
Alternative Solutions
Current API, as an alternative, is prone to return nil
, loosing all the accumulated results. For example, if you're iterating nodes in a form, when you reaching its end, you're loosing all the data since zipper is now nil
. A simple iteration over siblings would be to go right until you run out of nodes and then go up or go next without loosing any changes, nor the ability to move around. In the current API, a developer has to always keep reference to the previous zipper before performing any movement to not loose the data a zipper has and this makes the resulting code harder to reason about.