Since a Uniplate demo already exists, here is an implementation using the recursion-schemes library for completeness:
{-
The basic functor pattern is large, but relatively unsurprising and can save your efforts in the long run, once per type.
And here is another implementation using the geniplate library:
{-
And here is a shortened version of @bheklilr of a clearly recursive approach, which probably expects from a newbie (I used (++) for symmetry):
allSubTrees3 :: BinaryT a -> [BinaryT a] allSubTrees3 Empty = [] allSubTrees3 this @ (Node left _ right) = [this] ++ leftSubs ++ rightSubs where leftSubs = allSubTrees3 left rightSubs = allSubTrees3 right
Note that it contains the root directory, but it does not contain empty subtrees, but it is easily replaced.
I wonder what are the advantages and disadvantages of the various approaches. Is uniplate something more or less type safe than other approaches?
Note that the recursion-schemes approach is concise (if you need many different workarounds for the same type) and flexible (you have full control over the workaround, whether to include empty subtrees, etc.). One of the drawbacks is that the para type and other schemes are too general to allow type inference, so type signature is often required to disambiguate.
geniplate seems less intrusive than uniplate , since there is no need to put deriving clauses.
source share