|Fallintothis recently critiqued arc's tree utilities (http://arclanguage.org/item?id=18247) and brought to mind that I'd had similar niggles of my own.|
The common lisp way to do this would be with control abstractions like subst-if, as malisper pointed out (http://arclanguage.org/item?id=18246). But like Richard Gabriel, I dislike this approach: http://www.arclanguage.org/item?id=16304.
So what to do? I want to entirely eliminate names like ontree and treewise. I can never remember the names, the order of args, and so on. Hmm, is there some way I can bring my favorite defgeneric idea to bear on the problem? Turns out there is. Let's start with the simplest way we do traversals of lists:
How can we extend each to support trees? A 'tree' is really just an s-expression, just like a list. So we'll just say that if the caller wraps an s-expression in type tree, we'll do an infix traversal like ontree.
arc> (each x '(1 2 3) (prn x))
Now we can build ontree out of walk.
(def tree (x)
(annotate 'tree x))
(defmethod walk (seq f) tree ; each is built using walk
(let x rep.seq
(unless (atom x)
(walk (tree car.x) f)
(walk (tree cdr.x) f))))
Similarly, if we want to traverse just atoms like treewise (and like malisper wanted):
(def ontree (f x)
(walk (tree x) f))
I'm using arc's type system here, but I'm not creating new datatypes or 'objects'. Where variables of a user-defined datatype are created and used over potentially long lifetimes, the goal with these control-abstraction tags is to briefly wrap an underlying value in a navigation policy that the callee immediately takes off. This is an alternative to adding a new keyword arg, and I'm still mulling the pros and cons. The big advantage, I think, is that the control abstraction doesn't pollute the core definition of each primitive, but is only mentioned in methods that need it.
(def (leaves x)
(annotate 'leaves x))
(defmethod walk (seq f) leaves
(let x rep.seq
(if (atom x)
(do (walk (leaves car.x) f)
(walk (leaves cdr.x) f)))))
(def on-tree-leaves (f x)
(walk (tree-leaves x) f))
The definitions of ontree and its variant are just for illustration. My idea is to just use walk everywhere with appropriately specialized args. What do y'all think? https://github.com/arclanguage/anarki/commit/db172ec374. I'm considering similarly fixing find (for reclist), map (for tree-subst) and reduce (for treewise).