@@ -5,148 +5,4 @@ This implementation is inspired by
5
5
[ the original paper by Huet] ( https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf ) ,
6
6
as well as the [ Argonaut’s JSON Zipper] ( http://argonaut.io/doc/zipper/ ) .
7
7
8
- Consider the following example:
9
-
10
- ``` scala
11
- // Define a tree data structure
12
- case class Tree (x : Int , c : List [Tree ] = List .empty)
13
-
14
- // Create a tree
15
- val tree = Tree (
16
- 1 , List (
17
- Tree (
18
- 11 , List (
19
- Tree (111 ),
20
- Tree (112 )
21
- )
22
- ),
23
- Tree (
24
- 12 , List (
25
- Tree (121 ),
26
- Tree (
27
- 122 , List (
28
- Tree (1221 ),
29
- Tree (1222 )
30
- )
31
- ),
32
- Tree (123 )
33
- )
34
- ),
35
- Tree (13 )
36
- )
37
- )
38
- ```
39
-
40
- <img src =" ./docs/images/readme/tree.png " height =" 500px " />
41
-
42
- Since the tree is immutable, modifying it can be a pain,
43
- but it’s easily solved with a Zipper:
44
-
45
- ``` scala
46
- import zipper ._
47
-
48
- // Use a Zipper to move around and change data
49
- val modified = {
50
- Zipper (tree)
51
- .moveDownAt(1 ) // 12
52
- .moveDownRight // 123
53
- .deleteAndMoveLeft // 122
54
- .moveDownLeft // 1221
55
- .update(_.copy(x = - 1 ))
56
- .moveRight // 1222
57
- .set(Tree (- 2 ))
58
- .moveUp // 122
59
- .moveUp // 12
60
- .rewindLeft // 11
61
- .moveDownRight // 112
62
- .moveLeftBy(1 ) // 111
63
- .deleteAndMoveUp // 11
64
- .commit // commit the changes and return the result
65
- }
66
- ```
67
-
68
- Here’s what the modified tree looks like:
69
-
70
- <img src =" ./docs/images/readme/modified.png " height =" 500px " />
71
-
72
- If we draw both trees side by side, we’ll see that
73
- the unchanged parts are shared:
74
-
75
- <img src =" ./docs/images/readme/both.png " height =" 500px " />
76
-
77
- ### Usage
78
-
79
- Include these lines in your ` build.sbt ` :
80
-
81
- ``` scala
82
- // for JVM
83
- libraryDependencies += " io.github.stanch" %% " zipper" % " 0.5.2"
84
-
85
- // for Scala.js
86
- libraryDependencies += " io.github.stanch" %%% " zipper" % " 0.5.2"
87
- ```
88
-
89
- #### Unzip
90
-
91
- In order for the Zipper to work on your data structure ` Tree ` , you need an implicit instance of ` Unzip[Tree] ` .
92
- ` Unzip ` is defined as follows:
93
-
94
- ``` scala
95
- trait Unzip [A ] {
96
- def unzip (node : A ): List [A ]
97
- def zip (node : A , children : List [A ]): A
98
- }
99
- ```
100
-
101
- As we saw before, the library can automatically derive ` Unzip[Tree] `
102
- if the ` Tree ` is a case class that has a single field of type ` List[Tree] ` .
103
- It is also possible to derive an ` Unzip[Tree] ` for similar cases, but with other collections:
104
-
105
- ``` scala
106
- scala> case class Tree (x : Int , c : Vector [Tree ] = Vector .empty)
107
- defined class Tree
108
-
109
- scala> implicit val unzip = Unzip .For [Tree , Vector ].derive
110
- unzip: zipper.Unzip [Tree ] = zipper.GenericUnzipInstances $For $$anon$2 @ 6389ff1a
111
- ```
112
-
113
- The automatic derivation is powered by [ shapeless] ( https://github.com/milessabin/shapeless ) .
114
-
115
- #### Moves, failures and recovery
116
-
117
- There are many operations defined on a ` Zipper ` .
118
- Some of them are not safe, e.g. ` moveLeft ` will fail with an exception
119
- if there are no elements on the left.
120
- For all unsafe operations a safe version is provided, which is prefixed with ` try ` .
121
- These operations return a ` Zipper.MoveResult ` , which allows to recover from the failure or return to the original state:
122
-
123
- ``` scala
124
- scala> val tree = Tree (1 , Vector (Tree (3 ), Tree (4 )))
125
- tree: Tree = Tree (1 ,Vector (Tree (3 ,Vector ()), Tree (4 ,Vector ())))
126
-
127
- scala> val modified = {
128
- | Zipper (tree)
129
- | .moveDownLeft
130
- | .tryMoveLeft.getOrElse(_.insertLeft(Tree (2 )).moveLeft)
131
- | .commit
132
- | }
133
- modified: Tree = Tree (1 ,Vector (Tree (2 ,Vector ()), Tree (3 ,Vector ()), Tree (4 ,Vector ())))
134
- ```
135
-
136
- #### Loops
137
-
138
- ` Zipper ` provides a looping functionality, which can be useful with recursive data:
139
-
140
- ``` scala
141
- scala> val tree = Tree (1 , Vector (Tree (2 ), Tree (3 ), Tree (5 )))
142
- tree: Tree = Tree (1 ,Vector (Tree (2 ,Vector ()), Tree (3 ,Vector ()), Tree (5 ,Vector ())))
143
-
144
- scala> val modified = {
145
- | Zipper (tree)
146
- | .moveDownLeft
147
- | .repeatWhile(_.x < 5 , _.tryMoveRight)
148
- | .insertRight(Tree (4 ))
149
- | .commit
150
- | }
151
- modified: Tree = Tree (1 ,Vector (Tree (2 ,Vector ()), Tree (3 ,Vector ()), Tree (5 ,Vector ()), Tree (4 ,Vector ())))
152
- ```
8
+ See https://stanch.github.io/zipper for more details.
0 commit comments