I'm starting to use lenses and until now I've been unable to use them in a concrete part of a codebase I'm writing. My objective is to update a rose tree structure such as the one in Data.Tree
by adding a new node inside one of the existing ones. To do so I thought that it would make sense to identify each node with a unique id, so it would look like that:
type MyTree = Tree Id
type Path = [Id]
addToTree :: MyTree -> MyTree -> Path -> MyTree
addToTree originalTree newNode path = undefined
The function addToTree
would have to traverse the originalTree
by following the path of ids and add the newNode
at that level, returning the whole updated tree. I haven't had problems to make a getter for that but I'm not being able to find an appropriate lens to perform the operation with.
That's what I've got until now:
import Control.Lens
import Data.Tree
import Data.Tree.Lens
addToTree :: MyTree -> Path -> MyTree -> MyTree
addToTree tree path branch = tree & (traversalPath path) . branches %~ (branch:)
traversalPath :: (Foldable t, Applicative f, Contravariant f) => t Id -> (MyTree -> f MyTree) -> MyTree -> f MyTree
traversalPath = foldl (\acc id-> acc . childTraversal id) id
childTraversal :: (Indexable Int p, Applicative f) => Id -> p MyTree (f MyTree) -> MyTree -> f MyTree
childTraversal id = branches . traversed . withId id
withId :: (Choice p, Applicative f) => Id -> Optic' p f MyTree MyTree
withId id = filtered (\x -> rootLabel x == id)
But it fails to compile with:
• No instance for (Contravariant Identity) arising from a use of ‘traversalPath’ • In the first argument of ‘(.)’, namely ‘(traversalPath path)’ In the first argument of ‘(%~)’, namely ‘(traversalPath path) . branches’ In the second argument of ‘(&)’, namely ‘(traversalPath path) . branches %~ (branch :)’
Thanks!