The internal structure of the incoming sequence of selected nodes is not really relevant here. I'll try to convince you why:
Enlive is a templating library. It's main function is insert incoming data into some pre-defined template as quickly as possible. The resulting data is most often printed to a java.io.*Writer in a java Servlet Response object.
Conceptually an enlive template consists of small nested Clojure hash-maps representing XML-tags and all their attributes. These tags also keeps their (stringy) :content and also a ordered sequence of :children nodes to this tag. This data structure is navigated with a suitable zipper, that allows Enlive to navigate the tag-tree and implement efficient selector state-machines to find the selected nodes of the tag-tree (≈DOM). (A zipper conceptually just defines properties for navigating up, and
down, right and left in the data structure and whether a position is/has
a branch or not. The zipper itself also contains a "bread crumbs"
feature and can be flattened. Very, very suitable for navigating XML DOM
trees.)
The cool thing with zippers is that they allow us to make reasonable efficient changes to the tree which are also immutable, largely by reusing to old immutable parts of the zipper not changed in the operation, similar to how clojures vector, [], and hash-map, {}, does.
When Enlive as done all the transformations to a template, Enlive simply traverses (flattens) the tag-tree zipper from the root-tag and prints the various elements string representations to the print-writer output.
The good thing with Enlive is that it, when compiled to byte-code and the java just-in-time compilation kicks in, large parts of the "finaled" string representations could be chunked, even though the representation in the template works through many layers of clojure data structures. Essentially the template could be compiled down to cpu optimized machine code, keeping a sane (but powerful) interface to the developer. How cool isn't that?!
One thing left to answer though. Why do you see lazy sequences or persistent vectors and not some zipper-like structure in the transform functions you like to test?
It turns out the root of the zipper is hidden form the transform functions we supply. The transform functions just gets the nodes matched by the supplied selector and it should return a transformed version of each node.
The length of the sequence of matching tags could vary depending on the template as well as its previous transforms and their arguments, but it could also be just too memory intensive to realize all the nodes selected, and the vector of realizations would be of no use for the transform functions anyway. For small collections a PersisentVector is likely most efficient, for larger results a LazySequence could be more efficient. It's just a question of implementation, and could be subject to change without warning in coming releases of Enlive if new, more efficient data structures would emerge.
A reasonable strictness on your function would probably be to ensure both the incoming nodes and the results are a seq? (it's actually just a plan java-interface [2], strict enough, isn't it?). You could also make sure that the transform returned nodes qualifies as Enlive tags or what is kept as in between them in the resulting HTML document.
/Linus