Zipper implementation.
A zipper is a technique of representing an aggregate data structure so that it is convenient for writing programs that traverse the structure arbitrarily and update its contents, especially in purely functional programming languages.
Zipper
is a secret sauce that gives Tree
real power.
It provides an easy way to query and modify the Tree
in a clever and very flexible way.
Types within this module are exposed type aliases to make it easy extend default functionality of Zipper
.
Breadcrumbs are private type not meant to be manipulated directly.
However it's possible to extract breadcrubs from Zipper
in transformed
format using breadcrumbs
and indexedBreadcrumbs
functions which are meant for public use.
fromTree : Tree a -> Zipper a
Init Zipper
for Tree
.
import Tree as T
T.singleton "foo"
|> fromTree
|> current
--> "foo"
current : Zipper a -> a
Get item of current Tree
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> current
--> "foo"
children : Zipper a -> List a
Get children of current Tree
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> children
--> [ "bar" ]
isRoot : Zipper a -> Basics.Bool
Check if Zipper
is focused on root Tree
.
import Tree as T
T.singleton "foo"
|> fromTree
|> isRoot
--> True
isEmpty : Zipper a -> Basics.Bool
Check if current Tree
in Zipper
is empty.
import Tree as T
T.singleton "foo"
|> fromTree
|> isEmpty
--> True
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> isEmpty
--> False
attempt : (Zipper a -> Maybe (Zipper a)) -> Zipper a -> Zipper a
Attempt to perform action over zipper and return original Zipper
in cases where this action returns Nothing
.
import Tree as T
T.singleton "foo"
|> fromTree
|> attempt delete
|> current
--> "foo"
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> attempt (open ((==) "foo"))
|> attempt delete
|> current
--> "foo"
getTree : Zipper a -> Tree a
Extract current Tree
from a Zipper
.
useful in case where you don't want to use pattern mathcing
import Tree as T
T.singleton "foo"
|> fromTree
|> getTree
|> T.item
--> "foo"
insert : Tree a -> Zipper a -> Zipper a
Insert sub Tree
into current Tree
in Zipper
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> insert (T.singleton "baz")
|> children
--> [ "bar", "baz" ]
delete : Zipper a -> Maybe (Zipper a)
Delete current Tree
from Zipper
.
Returns Nothing if root node is removed.
import Tree as T
T.singleton "foo"
|> fromTree
|> delete
--> Nothing
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> open (always True)
|> Maybe.andThen delete
|> Maybe.map current
--> Just "foo"
update : (Tree a -> Tree a) -> Zipper a -> Zipper a
Update current Tree
using given function.
import Tree as T
T.singleton "foo"
|> fromTree
|> update (T.map (\a -> a ++ " fighter"))
|> current
--> "foo fighter"
updateItem : (a -> a) -> Zipper a -> Zipper a
Update item of current Tree
using given function.
import Tree as T
T.singleton "foo"
|> fromTree
|> updateItem (\i -> i ++ " fighter")
|> current
--> "foo fighter"
setTree : Tree a -> Zipper a -> Zipper a
Replace current Tree
with new one.
import Tree as T
T.singleton "foo"
|> fromTree
|> setTree (T.singleton "bar")
|> current
--> "bar"
open : (a -> Basics.Bool) -> Zipper a -> Maybe (Zipper a)
Open first children that satisfy given condition.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> open ((==) "bar")
|> Maybe.map current
--> Just "bar"
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz") )
|> attempt (open ((==) "bar"))
|> attempt (open ((==) "baz"))
|> current
--> "baz"
T.singleton "foo"
|> fromTree
|> open (always True)
--> Nothing
getPath : (a -> b) -> Zipper a -> List b
Use given function to convert current breadcrumb path to a list
Resulting list of breadcrumbs contains currently focused item as well.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> attemptOpenPath (==) [ "bar" ]
|> getPath identity
--> [ "foo", "bar" ]
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz") )
|> attemptOpenPath (==) [ "bar", "baz" ]
|> getPath identity
--> [ "foo", "bar", "baz" ]
openPath : (b -> a -> Basics.Bool) -> List b -> Zipper a -> Result b (Zipper a)
Open multiple levels reducing list by given function.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz") )
|> openPath (==) [ "bar", "baz" ]
|> Result.map current
--> Ok "baz"
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> openPath (==) [ "not-here", "baz" ]
|> Result.map current
--> Err "Can't resolve open for \"not-here\""
openAll : Zipper a -> List (Zipper a)
Get List
of Zipper
s for all children of current Zipper
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> insert (T.singleton "baz")
|> openAll
|> List.map current
--> [ "bar", "baz" ]
attemptOpenPath : (b -> a -> Basics.Bool) -> List b -> Zipper a -> Zipper a
Similar to openPath
but ingnore failed steps.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> attemptOpenPath (==) [ "not-here", "bar" ]
|> current
--> "bar"
T.singleton "foo"
|> fromTree
|> attemptOpenPath (==) [ "baz" ]
|> current
--> "foo"
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz"))
|> attemptOpenPath (==) [ "not-here", "bar", "missng", "baz" ]
|> current
--> "baz"
up : Zipper a -> Maybe (Zipper a)
Return back to parent of current Tree
in given Zipper
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar")
|> open ((==) "bar")
|> Maybe.andThen up
|> Maybe.map current
--> Just "foo"
T.singleton "baz"
|> fromTree
|> up
--> Nothing
upwards : Basics.Int -> Zipper a -> Maybe (Zipper a)
Perform up
n times.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz") )
|> open ((==) "bar")
|> Maybe.andThen (open ((==) "baz"))
|> Maybe.andThen (upwards 2)
|> Maybe.map current
--> Just "foo"
Returns given Zipper
return if 0
is passed:
T.singleton "foo"
|> fromTree
|> upwards 0
|> Maybe.map current
--> Just "foo"
Return Nothing
if there are not enough ancestors in Zipper
:
T.singleton 4
|> fromTree
|> upwards 1
--> Nothing
Return Nothing
if negative integer is passed:
T.singleton 4
|> fromTree
|> upwards -1
--> Nothing
root : Zipper a -> Zipper a
Back to root Tree
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz") )
|> open ((==) "bar")
|> Maybe.andThen (open ((==) "baz"))
|> Maybe.map root
|> Maybe.map current
--> Just "foo"
T.singleton "foo"
|> fromTree
|> root
|> current
--> "foo"
map : (a -> b) -> Zipper a -> Zipper b
Map function over Zipper
.
import Tree as T
T.singleton 1
|> fromTree
|> map ((+) 1)
|> current
--> 2
filter : (a -> Basics.Bool) -> Zipper a -> Zipper a
Performs filter on current Tree
in Zipper
. See Tree.filter
for more informations.
import Tree as T
T.Tree 1 [ T.singleton 2, T.singleton 3, T.singleton 4 ]
|> fromTree
|> filter ((>) 4)
|> children
--> [ 2, 3 ]
T.Tree 1 [ T.singleton 2, T.singleton 3, T.singleton 4 ]
|> fromTree
|> attempt (open ((==) 1))
|> filter ((<) 2)
|> root
|> children
--> [ 3, 4 ]
T.Tree 1 [ T.insert (T.singleton 5) <| T.singleton 2, T.insert (T.singleton 6) <| T.singleton 3, T.singleton 4 ]
|> fromTree
|> attempt (open ((==) 1))
|> filter ((<) 2)
|> getTree
|> T.descendants
|> List.andThen (List.map T.item << T.descendants)
--> [ 6 ]
breadcrumbs : Zipper a -> List a
Get List
of Breacrub
s .
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz"))
|> attemptOpenPath (==) [ "bar", "baz" ]
|> breadcrumbs
--> [ "bar", "foo" ]
indexedBreadcrumbs : Zipper a -> List ( Basics.Int, a )
Get Breacrub
s as indexed List
.
import Tree as T
T.singleton "foo"
|> fromTree
|> insert (T.singleton "bar" |> T.insert (T.singleton "baz"))
|> attemptOpenPath (==) [ "bar", "baz" ]
|> indexedBreadcrumbs
--> [ ( 1, "bar" ), ( 2, "foo" )]