Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zippers

 Zippers

Zippers are a design pattern in functional programming languages, such as Haskell, which provides a focus point and methods for navigating around in a functional data structure. It turns out that for any algebraic data type with one parameter, the derivative of the type is a zipper for it.

David Overton

August 01, 2013
Tweet

More Decks by David Overton

Other Decks in Programming

Transcript

  1. Zippers
    David Overton
    1 August 2013

    View full-size slide

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

    View full-size slide

  3. 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’;

    View full-size slide

  4. 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.

    View full-size slide

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

    View full-size slide

  6. 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)

    View full-size slide

  7. 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)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. 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.)

    View full-size slide

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

    View full-size slide

  14. 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)

    View full-size slide

  15. 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)

    View full-size slide

  16. *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

    View full-size slide

  17. *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

    View full-size slide

  18. *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

    View full-size slide

  19. 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 | · · ·

    View full-size slide

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

    View full-size slide

  21. 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)

    View full-size slide

  22. 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)

    View full-size slide

  23. 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 + · · ·

    View full-size slide

  24. 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!

    View full-size slide

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

    View full-size slide

  26. 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

    View full-size slide

  27. 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)

    View full-size slide

  28. 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 . . . ?

    View full-size slide

  29. 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!

    View full-size slide

  30. 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])

    View full-size slide

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

    View full-size slide

  32. 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.

    View full-size slide