Bradley Hardy
Bradley Hardy

Reputation: 765

Can I get a `Traversal` to items stored in branches using `Control.Lens.Plated`?

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?

Upvotes: 3

Views: 173

Answers (1)

Hjulle
Hjulle

Reputation: 2570

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.

Upvotes: 3

Related Questions