I am trying to make a function that take a index into a forest, and a list of integers to choose a way down a tree, and turns that into a lens that focuses that same element.
Effectively, it is the opposite of what (t :: Data.Tree.Forest) ^@.. itraversed <.> itraversed
does.
Here is my attempt:
pathToLens :: (Applicative f) => (Int, [Int]) -> LensLike' f (Data.Tree.Forest a) (Data.Tree.Tree a)
pathToLens (rootIdx, branchIndices) =
ix rootIdx . helper branchIndices
where
helper :: (Applicative f) => [Word64] -> LensLike' f (Data.Tree.Tree a) (Data.Tree.Tree a)
helper (idx : rest) = branches . ix idx . helper rest
helper [] = id
The function compiles fine. But then I try to use it:
test = [Data.Tree.Node () []] ^. (pathToLens (0,[]))
And I get:
• No instance for (Monoid (Data.Tree.Tree ()))
arising from a use of ‘pathToLens’
• In the second argument of ‘(^.)’, namely ‘(pathToLens (0, []))’
Why is there no Monoid for my Tree? From looking at the Tree docs, it seems there is Monad Tree. Does that not include Monoid Tree?
Given that a tree at the given path might not exist, pathToLens
can't be a proper lens. Lenses always hit their targets.
It can be made a Traversal'
:
import Data.List.NonEmpty
pathToTraversal :: NonEmpty Int -> Traversal' (Data.Tree.Forest a) (Data.Tree.Tree a)
pathToTraversal (rootIdx :| branchIndices) =
ix rootIdx . helper branchIndices
where
helper (idx : rest) = branches . ix idx . helper rest
helper [] = id
And, if one is feeling adventurous and believes the Traversal'
will always hit a target, it can be turned into a Lens'
using unsafeSingular
:
unsafePathToLens :: NonEmpty Int -> Lens' (Data.Tree.Forest a) (Data.Tree.Tree a)
unsafePathToLens is = unsafeSingular (pathToTraversal is)