I can easily get a Traversal to items stored in leaves:
data Tree a = Br (Tree a) (Tree a) | Lf a
deriving (Data)
instance Plated (Tree a) where
makePrisms ''Tree
leaves :: Traversal' (Tree a) a
leaves = deep _Lf
But deep can't keep going if I take something out of a branch. The best I can seem to achieve with data in branches is a Fold:
data Tree a = Br a (Tree a) (Tree a) | Lf
deriving (Data)
instance Plated (Tree a) where
makePrisms ''Tree
branchData :: Fold (Tree a) a
branchData = cosmos._Br._1
It is obviously possible to manually construct a Traversal because this Tree can be made Traversable:
instance Traversable Tree where
sequenceA Lf = pure Lf
sequenceA (Br x l r) = Br <$> x <*> sequenceA l <*> sequenceA r
Can Plated do it for us automatically somehow?
I do not believe it is possible to do using Plated, but it is trivial to do using template from Data.Data.Lens:
branchData :: Data a => Traversal' (Tree a) a
branchData = template
Template finds all elements of a type that can be accessed without going through the same type.
The closest thing to a traversal that I can get from Plated (except for the cosmos fold you mentioned) is transformM :: (Monad m, Plated a) => LensLike' m a a. It can be used as a Setter and can do some other stuff.
The reason that transformM needs a Monad (instead of an Applicative that would be required for a traversal) is that we need to update the children before updating the node, which means that ordering is needed. It is impossible to make a traversal that points at both a node and a child of that node.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With