At its most abstract level, a Traverser maintains the following data:
1. A reference to its current location in the graph (Traverser.get())
2. A reference to its current location in the traversal (Traverser.getStepId())
3. A bulk denoting how many traversers it represents (Traverser.bulk())
4. A loop stack denoting how many times its gone through a repeat sequence (Traverser.loops())
5. A path denoting the history of the traverser’s travel through the graph (Traverser.path())
6. A sack denoting a traverser local side-effect data structure (Traverser.sack())
This is a lot of data to maintain especially when there can be millions of traversers flowing through the system. Fortunately, most traversals don’t require all 6 concepts of the traverser. Thus, there are different “species” of Traversers that have varying amounts of the above data represented.
O_Traverser is the simplest — only maintaining data for 1 and 2.
B_O_Traverser is a bit more — only maintaining data for 1, 2, and 3.
…
so forth and so on.
Thus, while theoretically, a Traverser has all 6 items of data, in practice, at compile time, we can choose the “slimmest” (light weight) traverser species to use for the respective Traversal in order to reduce the memory footprint. This becomes all the more important in distributed traversal execution where traversers jump between machines.
You might ask — “how do you know which traverser species to use for a traversal?” … Thats all about Traversal.getRequirements().
I hope that is clear,
Marko.