Slide 1

Slide 1 text

Zippers David Overton 1 August 2013

Slide 2

Slide 2 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 3

Slide 3 text

Motivation Navigating around in and updating mutable data structures is easy: • Keep a pointer to your current location in the data structure; • Update the pointer to move around the data structure; • Modify the data structure using destructive update via the pointer. Example (C Array) char my_array[1024]; char *p = my_array; // Move right p++; // Move left p--; // Update focused element *p = ’A’;

Slide 4

Slide 4 text

Motivation But what is the equivalent for an immutable data structure? I.e. how do we keep track of a local focus point within a data structure? E.g. XML document tree, text editor buffer, window manager stack (e.g. XMonad), AVL tree. For a list we could use an integer to represent the element we are interested in: type ListFocus a = (Int, [a]) but this requires traversing from the start of the list every time – an O(n) operation. For more complex types, this is even less practical. Instead, we can use a data structure called a zipper.

Slide 5

Slide 5 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 6

Slide 6 text

List Zipper -- A list zipper is a pair consisting of the front -- of the list (reversed) and the rear of the list. type ListZipper a = ([a], [a]) -- Convert a list to a zipper. -- Start at the front of the list. fromList :: [a] → ListZipper a fromList xs = ([], xs)

Slide 7

Slide 7 text

List Zipper -- Move about in the zipper. right, left :: ListZipper a → ListZipper a right (xs, y:ys) = (y:xs, ys) left (x:xs, ys) = (xs, x:ys) -- Modify the focused element. modify :: (a → a) → ListZipper a → ListZipper a modify f (xs, y:ys) = (xs, (f y):ys) modify _ zs = zs -- When we are finished, convert back to a list. toList :: ListZipper a → [a] toList ([], ys) = ys toList zs = toList (left zs)

Slide 8

Slide 8 text

*Main> let z = fromList [’a’, ’b’, ’c’] *Main> z ("","abc") a ctx list b c

Slide 9

Slide 9 text

*Main> right z ("a","bc") a ctx list b c

Slide 10

Slide 10 text

*Main> right $ right z ("ba","c") a ctx list b c

Slide 11

Slide 11 text

*Main> right $ right $ right z ("cba","") a ctx list b c

Slide 12

Slide 12 text

List Zipper Example (XMonad) XMonad is a tiling window manager for X written in Haskell. The main data structure for managing workspace and window focus uses nested list zippers. type StackSet a = ListZipper (Workspace a) type Workspace a = ListZipper (Window a) This allows XMonad to keep track of the focused workspace in an ordered list of workspaces and also keep track of the focused window on each workspace. (This is a simplification of the actual types used. The zippers that XMonad uses allow wrapping when you reach the end of the window or workspace list.)

Slide 13

Slide 13 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 14

Slide 14 text

Binary Tree data Tree a = Empty | Node (Tree a) a (Tree a) deriving Show type Context a = Either (a, Tree a) (a, Tree a) type TreeZipper a = ([Context a], Tree a) fromTree :: Tree a → TreeZipper a fromTree t = ([], t) right, left, up :: TreeZipper a → TreeZipper a right (ctx, Node l a r) = (Right (a, l) : ctx, r) left (ctx, Node l a r) = (Left (a, r) : ctx, l)

Slide 15

Slide 15 text

Binary Tree up (Left (a, r) : ctx, l) = (ctx, Node l a r) up (Right (a, l) : ctx, r) = (ctx, Node l a r) modify :: (a → a) → TreeZipper a → TreeZipper a modify f (ctx, Node l a r) = (ctx, Node l (f a) r) modify f z = z toTree :: TreeZipper a → Tree a toTree ([], t) = t toTree z = toTree (up z)

Slide 16

Slide 16 text

*Main> let t = Node (Node (Node Empty ’d’ Empty) ’b’ (Node Empty ’e’ Empty)) ’a’ (Node (Node Empty ’f’ Empty) ’c’ (Node Empty ’g’ Empty)) *Main> fromTree t a c b e d g f ctx tree

Slide 17

Slide 17 text

*Main> right $ fromTree t ( [Right (’a’, Node (Node Empty ’d’ Empty) ’b’ (Node Empty ’e’ Empty))], Node (Node Empty ’f’ Empty) ’c’ (Node Empty ’g’ Empty) ) Right a c b e d g f ctx tree

Slide 18

Slide 18 text

*Main> left $ right $ fromTree t ( [ Left (’c’, Node Empty ’g’ Empty), Right (’a’, Node (Node Empty ’d’ Empty) ’b’ (Node Empty ’e’ Empty))], Node Empty ’f’ Empty ) Right Left a c b e d g f ctx tree

Slide 19

Slide 19 text

Tree Zippers Example (XML) • XPath navigation around an XML document is basically a zipper – you have a current context and methods to navigate to neighbouring nodes. • One implementation of this in Haskell is the rose tree zipper from HXT. data NTree a = NTree a [NTree a] -- A rose tree data NTCrumb = NTC [NTree a] a [NTree a] -- "Breadcrumb" for context -- The zipper itself data NTZipper a = NTZ { ntree :: NTree a, context :: [NTCrumb a] } -- And using it to represent XML type XmlTree = NTree XNode type XmlNavTree = NTZipper XNode data XNode = XText String | XTag QName [XmlTree] | XAttr QName | · · ·

Slide 20

Slide 20 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 21

Slide 21 text

Algebraic Data Types Why are the types in languages such as Haskell sometimes referred to as Algebraic Data Types? One way to see the relationship to algebra is to convert the types to algebraic expressions which represent the number of values that a type has. data Void ⇔ 0 The empty type data () = () ⇔ 1 The unit type data Either a b = Left a | Right b ⇔ a + b A sum type type Pair a b = (a, b) ⇔ a.b A product type Technically, Haskell types form an algebraic structure called a semiring. Some more examples: data Bool = False | True ⇔ 2 data Maybe a = Nothing | Just a ⇔ 1 + a type Func a b = a -> b ⇔ ba An exponential type Int ⇔ 232 or 264 (implementation dependent)

Slide 22

Slide 22 text

Algebraic Laws The usual algebraic laws (for semirings) hold, up to type “equivalence”: 0 + a = a ⇔ Either Void a ∼ = a a + b = b + a ⇔ Either a b ∼ = Either b a 0.a = 0 ⇔ (Void, a) ∼ = Void 1.a = a ⇔ ((), a) ∼ = a a.b = b.a ⇔ (a, b) ∼ = (b, a) a.(b + c) = a.b + a.c ⇔ (a, Either b c) ∼ = Either (a,b) (a,c) (cb)a = cb.a ⇔ a -> b -> c ∼ = (a, b) -> c a2 = a.a ⇔ Bool -> a ∼ = (a, a)

Slide 23

Slide 23 text

Algebra of the List Type data List a = Nil | Cons a (List a) L(a) = 1 + a.L(a) = 1 + a.(1 + a.L(a)) = 1 + a + a2(1 + a.L(a)) = 1 + a + a2 + a3 + a4 + · · · So a list has either 0 elements or 1 element or 2 elements or . . . (which we already knew!). Alternatively, solving for L(a): L(a) − a.L(a) = 1 (1 − a).L(a) = 1 L(a) = 1 1 − a It doesn’t seem to make much sense to do subtraction or division on types. But look at the Taylor series expansion: 1 1 − a = 1 + a + a2 + a3 + a4 + · · ·

Slide 24

Slide 24 text

Algebra of Tree Types data Tree a = Empty | Node (Tree a) a (Tree a) T(a) = 1 + a.T(a)2 a.T(a)2 − T(a) + 1 = 0 T(a) = 1 − √ 1 − 4.a 2.a = 1 + a + 2a2 + 5a3 + 14a4 + · · · via the quadratic formula and Taylor series expansion. But now we are taking square roots of types!

Slide 25

Slide 25 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 26

Slide 26 text

One-Hole Contexts Definition The one-hole context of a parameterised type T(a) is the type of data structures you get if you remove one distinguished element of type a from a data structure of type T(a) and somehow mark the hole where the element came from. Example type Triple a = (a, a, a) The one-hole contexts are ( , a, a), (a, , a), and (a, a, ). A type that could represent this is data TripleOhc a = Left a a | Mid a a | Right a a

Slide 27

Slide 27 text

One-Hole Contexts Look at the algebraic types: type Triple a = (a, a, a) ⇔ a3 type TripleOhc a = Left a a | Mid a a | Right a a ⇔ 3a2 Notice that 3a2 = d da a3 In fact, this this relationship holds for any parameterised type T(a). Observation The type of the one-hole context of any parameterized type T(a) is the derivative type d da T(a) or ∂aT(a)

Slide 28

Slide 28 text

Zippers from One-Hole Contexts Observation For any parameterized type T(a), the type a.∂aT(a) is a zipper for type T(a). Example The type type TripleZipper a = (a, TripleOhc a) is a zipper for the type Triple a. TripleZipper(a) = a.3a2 = 3a3 = 3.Triple(a) But what about some more interesting types . . . ?

Slide 29

Slide 29 text

List Zipper L(a) = 1 1 − a ∂aL(a) = 1 (1 − a)2 = L(a)2 ZL(a) = a.∂aL(a) = a.L(a)2 type ListZipper a = (a, [a], [a]) This is slightly different from our original list zipper type ([a], [a]). The distinguished element is now pulled out of the second list and made explicit. This also means that the zipper can’t be empty. However, this is still a list zipper and we derived it using the differential calculus!

Slide 30

Slide 30 text

Tree Zipper T(a) = 1 + a.T(a)2 ∂aT(a) = T(a)2 + 2a.T(a).∂aT(a) ∂aT(a) = T(a)2 1 − 2a.T(a) = T(a)2.L(2a.T(a)) ZT (a) = a.∂aT(a) = a.T(a)2.L(2a.T(a)) type Context a = Either (a, Tree a) (a, Tree a) type TreeZipper a = (a, Tree a, Tree a, [Context a])

Slide 31

Slide 31 text

Table of Contents 1 Motivation 2 List Zipper 3 Tree Zippers 4 Algebraic Data Types 5 Type Differentiation 6 Conclusion

Slide 32

Slide 32 text

Conclusion • Zippers are a useful way of navigating and manipulating mutable data structures in real-world functional programs. • Algebraic data types really are algebraic. • Differentiation of types can lead to automatic derivation of zippers. • Nobody really knows why this works yet — active research.