Search code examples
haskelltreehaskell-lens

How do I convert indices to a lens for a Forest to a Tree?


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?


Solution

  • 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)