tomjkidd / elm-multiway-tree-zipper / MultiwayTreeZipper

A library for navigating and updating immutable trees. The elements in the tree must have the same type. The trees are implemented in a Huet Zipper fashion.

Types


type Context a
    = Context a (List (MultiwayTree.Tree a)) (List (MultiwayTree.Tree a))

The necessary information needed to reconstruct a MultiwayTree as it is navigated with a Zipper. This context includes the datum that was at the previous node, a list of children that came before the node, and a list of children that came after the node.


type alias Breadcrumbs a =
List (Context a)

A list of Contexts that is contructed as a MultiwayTree is navigated. Breadcrumbs are used to retain information about parts of the tree that move out of focus. As the tree is navigated, the needed Context is pushed onto the list Breadcrumbs, and they are maintained in the reverse order in which they are visited


type alias Zipper a =
( MultiwayTree.Tree a
, Breadcrumbs a 
)

A structure to keep track of the current Tree, as well as the Breadcrumbs to allow us to continue navigation through the rest of the tree.

Navigation API

goToChild : Basics.Int -> Zipper a -> Maybe (Zipper a)

Move down relative to the current Zipper focus. This allows navigation from a parent to it's children.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 1)

goUp : Zipper a -> Maybe (Zipper a)

Move up relative to the current Zipper focus. This allows navigation from a child to it's parent.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 0)
    |> andThen goUp

goToRoot : Zipper a -> Maybe (Zipper a)

Move to the root of the current Zipper focus. This allows navigation from any part of the tree back to the root.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b"
            [ Tree "e" [] ]
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 0)
    |> andThen (goToChild 1)
    |> andThen (goToRoot)

goLeft : Zipper a -> Maybe (Zipper a)

Move left relative to the current Zipper focus. This allows navigation from a child to it's previous sibling.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 1)
    |> andThen (goLeft)

goRight : Zipper a -> Maybe (Zipper a)

Move right relative to the current Zipper focus. This allows navigation from a child to it's next sibling.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 1)
    |> andThen (goRight)

goToNext : Zipper a -> Maybe (Zipper a)

Moves to the next node in the hierarchy, depth-first. If already at the end, stays there.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToNext)
    |> andThen (goToNext)

goToPrevious : Zipper a -> Maybe (Zipper a)

Moves to the previous node in the hierarchy, depth-first.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 2)
    |> andThen (goToPrevious)
    |> andThen (goToPrevious)

goToRightMostChild : Zipper a -> Maybe (Zipper a)

Move down and as far right as possible relative to the current Zipper focus. This allows navigation from a parent to it's last child.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b" []
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToRightMostChild)

goTo : (a -> Basics.Bool) -> Zipper a -> Maybe (Zipper a)

Move the focus to the first element for which the predicate is True. If no such element exists returns Nothing. Starts searching at the root of the tree.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b"
            [ Tree "e" [] ]
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goTo (\elem -> elem == "e"))

Update API

updateDatum : (a -> a) -> Zipper a -> Maybe (Zipper a)

Update the datum at the current Zipper focus. This allows changes to be made to a part of a node's datum information, given the previous state of the node.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b"
            [ Tree "e" [] ]
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 0)
    |> andThen (updateDatum (\old -> old ++ "X")) -- Appends an X to "b"
    |> andThen (goToRoot)

replaceDatum : a -> Zipper a -> Maybe (Zipper a)

Replace the datum at the current Zipper focus. This allows complete replacement of a node's datum information, ignoring the previous state of the node.

import Maybe exposing (andThen)

simpleTree =
    Tree "a"
        [ Tree "b"
            [ Tree "e" [] ]
        , Tree "c" []
        , Tree "d" []
        ]

Just (simpleTree, [])
    |> andThen (goToChild 0)
    |> andThen (replaceDatum "X") -- Replaces "b" with "X"
    |> andThen (goToRoot)

insertChild : MultiwayTree.Tree a -> Zipper a -> Maybe (Zipper a)

Inserts a Tree as the first child of the Tree at the current focus. Does not move the focus.

appendChild : MultiwayTree.Tree a -> Zipper a -> Maybe (Zipper a)

Inserts a Tree as the last child of the Tree at the current focus. Does not move the focus.

updateChildren : MultiwayTree.Forest a -> Zipper a -> Maybe (Zipper a)

Fully replace the children at the current Zipper focus.

Access API

datum : Zipper a -> a

Access the datum at the current Zipper focus.

maybeDatum : Zipper a -> Maybe a

Access the datum at the current Zipper focus as a Maybe.

References

The Zipper, Gerard Huet Learn You A Haskell, Zippers, Miran Lipovaca

Future work

Might be able to integrate existing Rose Tree to work with the Zipper. Wanted the first version to be self contained.