{"id":839,"date":"2010-01-13T20:23:09","date_gmt":"2010-01-13T20:23:09","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2010\/01\/13\/zippers-making-functional-updates-efficient\/"},"modified":"2010-01-13T20:23:09","modified_gmt":"2010-01-13T20:23:09","slug":"zippers-making-functional-updates-efficient","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2010\/01\/13\/zippers-making-functional-updates-efficient\/","title":{"rendered":"Zippers: Making Functional &quot;Updates&quot; Efficient"},"content":{"rendered":"<p><img decoding=\"async\" src=\"http:\/\/upload.wikimedia.org\/wikipedia\/commons\/f\/f0\/Zipper_animated.gif\" class=\"inset right\" \/><\/p>\n<p> In the Haskell stuff, I was planning on moving on to some monad-related<br \/>\nstuff. But I had a reader write in, and ask me to write another<br \/>\npost on data structures, focusing on a structured called a<br \/>\n<em>zipper<\/em>.<\/p>\n<p> A zipper is a remarkably clever idea. It&#8217;s not really a single data<br \/>\nstructure, but rather a way of building data structures in functional<br \/>\nlanguages. The first mention of the structure seems to be <a href=\"http:\/\/www.st.cs.uni-saarland.de\/edu\/seminare\/2005\/advanced-fp\/docs\/huet-zipper.pdf\">a paper<br \/>\nby Gerard Huet in 1997<\/a>, but as he says in the paper, it&#8217;s likely that this was<br \/>\nused before his paper in functional code &#8212; but no one thought to formalize it<br \/>\nand write it up. <em>(In the original version of this post, I said the name of the guy who first wrote about zippers was &#8220;Carl Huet&#8221;. I have absolutely no idea where that came from &#8211; I literally had his paper <b>on my lap<\/b> as I wrote this post, and I still managed to screwed up his name. My apologies!)<\/em><\/p>\n<p> It also happens that zippers are one of the rare cases of data structures<br \/>\nwhere I think it&#8217;s <em>not<\/em> necessarily clearer to show code. The concept of<br \/>\na zipper is very simple and elegant &#8211; but when you see a zippered tree<br \/>\nwritten out as a sequence of type constructors, it&#8217;s confusing, rather<br \/>\nthan clarifying.<\/p>\n<p><!--more--><\/p>\n<p> The basic idea of a zipper is to give you a way of efficiently working with data<br \/>\nstructures in a functional language. There are a lot of cases where in an imperative<br \/>\nlanguage, there&#8217;s some basic operation which is cheap and simple in the imperative<br \/>\nlanguage, because it&#8217;s performed by an in-place update. But in a functional language,<br \/>\nyou can&#8217;t update a field of a data structure: instead, you have to create a new copy of the structure with the altered<br \/>\nfield. <\/p>\n<p> For example, consider the list <code>[a b c d e f g]<\/code>. Implemented<br \/>\nas a cons-list, it&#8217;s a list of 7 cons-cells. Suppose you wanted<br \/>\nto replace &#8220;e&#8221; with &#8220;q&#8221;. In an imperative language, that&#8217;s no problem: just<br \/>\ndo a set-car! of the 5th cell. In a functional language, you would<br \/>\nneed to create a new list with &#8220;q&#8221; instead of<br \/>\n&#8220;e&#8221;. You could re-use the common tail <code>[f g]<\/code>, but you would need<br \/>\nto re-create the other 5 cells: you&#8217;d need to create a new cell to<br \/>\nattach &#8220;q&#8221; to <code>[f g]<\/code>. Then you&#8217;d need to create a new<br \/>\ncell to connect &#8220;d&#8221; to <code>[q f g]<\/code>. And so on.<\/p>\n<p> That makes the functional program much slower than the imperative one.<br \/>\nIf you&#8217;ve got a data structure that conceptually changes over time, and you&#8217;re going to make lots of changes,<br \/>\nthe cost of doing it functionally can become very high, because of all of the copying<br \/>\nyou do instead of mutating a data structure.<\/p>\n<p> In general, it&#8217;s very hard to get around that. You can&#8217;t update in place<br \/>\nin a functional language (at least, not without some serious cleverness, either<br \/>\nin your code (like monads), you language (like linear types), or your compiler).<br \/>\nBut for many applications, there&#8217;s some notion<br \/>\nof a <em>focus point<\/em> &#8211; that is, a particular key point where changes<br \/>\nhappen &#8212; and you can build structures where updates <em>around the focus<\/em><br \/>\ncan be performed efficiently.<\/p>\n<p> For example, if you&#8217;re building a text editor, you&#8217;ve got the point<br \/>\nwhere the cursor is sitting &#8211; and the changes all happen around the cursor.<br \/>\nThe user might type some characters, or delete some characters &#8211; but it always<br \/>\nhappens around the cursor.<\/p>\n<p> What a zipper does is take a data structure, and unfold it around a focal<br \/>\npoint. Then you can make changes at the focal point very quickly &#8211; about as<br \/>\nquickly as an in-place update in an imperative language.<\/p>\n<p> The idea of it is a lot like a gap-buffer. Right now, I&#8217;m actually working<br \/>\non a text-editor. I&#8217;m writing it using a gap-buffer. Conceptually, an<br \/>\nedit-buffer is one continuous sequence of characters. But if you represent it<br \/>\nas a continuous sequence of characters, every insert is extremely expensive.<br \/>\nSo what you do is split it into two sub-sequences: one consisting of the<br \/>\ncharacters <em>before<\/em> the cursor point, and one consisting of the<br \/>\ncharacters <em>after<\/em> the cursor point. With that representation,<br \/>\ninserting a character at the cursor point is O(1). Moving by one character is<br \/>\nalso O(1). Moving by N characters is O(N). With various improvements, you can<br \/>\ndo much better than that &#8211; but the key bit is that split between before the<br \/>\nfocus point and after it.<\/p>\n<p> A zipper is a tree or graph-based version of a similar idea. For this<br \/>\ndiscussion, I&#8217;ll describe it in terms of trees; the graph version is more complicated,<br \/>\nbut you should be able to get the idea from seeing how it works on trees. The<br \/>\nidea is that you take the tree structure, and you split it around a focus. You&#8217;re focused on some node in the<br \/>\ntree. You keep track of a set of nodes that come <em>before<\/em> you, and a<br \/>\nset of nodes that come <em>after<\/em> you &#8211; those are basically like the<br \/>\npre-gap and post-gap regions of a gap buffer. But because you&#8217;re working in a<br \/>\ntree, you need a bit more information: you need to know the <em>path<\/em> from<br \/>\nthe root of the tree down to the current node. <\/p>\n<p> It&#8217;s called a zipper because what you do to create this pre-focus, path,<br \/>\nand post-focus bits of the structure is <em>unzip<\/em> the tree. For example,<br \/>\nlook at the tree below. It&#8217;s a representation of a string of text represented<br \/>\nby a tree. In this particular tree, all of the data is stored in the leaves.<br \/>\nThe internal nodes contain metadata, which I haven&#8217;t shown in the diagram.<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_413.png?resize=389%2C202\" width=\"389\" height=\"202\" alt=\"string-tree.png\" \/><\/div>\n<p> Now, suppose I want to put the focus on &#8220;mno&#8221;. To do that, I climb down<br \/>\nthe tree, <em>unzipping<\/em> as I go. I start at the root, node N1. Then I go<br \/>\nright. So I put N1 and its left subtree into the left-context of my<br \/>\nzipper-tree, and add &#8220;Right at N1&#8221; to the path. That puts the focus at N3. To<br \/>\nget to &#8220;mno&#8221; from N3, I need to go left. So I put N3 and its right child into<br \/>\nthe right context, and add &#8220;Left at N3&#8221; to the path. Now the focus is at N4.<br \/>\nTo get to &#8220;mno&#8221;, I need to go right: so I put N4 and its left child into the<br \/>\nleft context, and add &#8220;Right at N4&#8221; to the path. Now I&#8217;ve got the focus set<br \/>\nwhere I want it at &#8220;mno&#8221;; and I&#8217;ve got right and left contexts.<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_414.png?resize=447%2C231\" width=\"447\" height=\"231\" alt=\"string-tree-zippered.png\" \/><\/div>\n<p> With the zipper, you can make all sorts of changes very easily at the<br \/>\nfocus. Suppose I want to change the focus node, by inserting some text. I can<br \/>\ndo that functionally, without actually changing anything, by creating a new<br \/>\nzipper tree which is identical to the old one, but which changes the value of<br \/>\nthe focus node &#8211; that is, if I were to add &#8220;123&#8221; right after &#8220;mno&#8221;, I could do<br \/>\nit by creating a new focus node &#8220;mno123&#8221;, with the same path, left, and right<br \/>\ncontexts. It takes minimal extra memory to create the copy, because I can<br \/>\nre-use the path and the contexts. <\/p>\n<p> I could also add new children nodes. Suppose that instead of adding<br \/>\n&#8220;123&#8221; to the focus, I want to keep each leaf containing three characters.<br \/>\ncould replace the focus with a new node, N5, which had children &#8220;mno&#8221;<br \/>\nand &#8220;123&#8221;. I could re-use the &#8220;mno&#8221; node, and the path, left, and right<br \/>\ncontexts.<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_415.png?resize=497%2C231\" width=\"497\" height=\"231\" alt=\"string-tree-zippered-and-edited.png\" \/><\/div>\n<p> That&#8217;s the beauty of the zipper: most operations can be in terms of local<br \/>\nchanges, re-using most of the structure. If we were using a standard tree,<br \/>\nthen to add a new node in the position of &#8220;mno&#8221;, we would need to create<br \/>\ncopies of N4, N3, and N1; instead, we only need to create the one new<br \/>\nnode.<\/p>\n<p> Doing other things isn&#8217;t that difficult either. Suppose we wanted to move<br \/>\nthe focus to &#8220;pqr&#8221;. We&#8217;d need to shift the focus from &#8220;mno&#8221; to N3, then to N3,<br \/>\nand then to &#8220;pqr&#8221;. To get from &#8220;mno&#8221; to N4, we take the last step off of the<br \/>\npath &#8211; which says we went right at N4 &#8211; so we set the focus to N4, and<br \/>\nre-establish &#8220;mno&#8221; as its right child. So the focus would be N4, with &#8220;jkl&#8221; as<br \/>\nits left child, and &#8220;mno&#8221; as its right child. To get from N4 to N3, we unroll<br \/>\nanother step of the path: since we went left at N3, that means that N3 is the<br \/>\nnew focus, with N4 as its left child. Then we&#8217;d go down to the right from N3,<br \/>\nso we&#8217;d add &#8220;right at N3&#8221; to the path, and &#8220;pqr&#8221; would be the new focus.<br \/>\nMoving the focus like that is a tad more difficult than just traversing<br \/>\nnon-zipper tree, but it&#8217;s not significantly slower &#8211; and it makes the edits<br \/>\nmuch, <em>much<\/em> faster.<\/p>\n<p> So why is it harder to code? Because when we&#8217;re dealing with trees, we&#8217;re pretty much always dealing with balance. And balance <em>isn&#8217;t<\/em> a local property. No matter which kind of tree you use &#8211; red\/black, 2\/3, AVL &#8211;  you might need to climb up the tree to do the balance maintenance. That mangles the simple zipper.<\/p>\n<p> You&#8217;ve got two choices. One is to re-balance<br \/>\nthe tree immediately. You can definitely do that. For<br \/>\nexample, if you think of how you do a re-balance in<br \/>\na red-black tree, you climb up the tree doing fixes until you&#8217;ve got things rebalanced. You can definitely do that &#8211; by using the zipper to move around the tree. But a big part of the point of the zipper is to keep operations local, and the re-balancing is not a<br \/>\nlocal operation. Much of the time, you can do things<br \/>\nlocally, but sometimes you&#8217;ll be stuck re-zipping as you move the focus up the tree fixing the balance; in<br \/>\nthe worst case, you need to re-zip the entire tree, all the way to the root.<\/p>\n<p> The alternative is something called <em>scarring<\/em>. You put marks in the tree called scars that identify places where you made changes that could trigger a rebalance. (Or more generally,<br \/>\nin places where you made an edit that could have violated some invariant of the data structure.)  You don&#8217;t do the fix immediately &#8211; you just mark it with<br \/>\nthe scar, and then at some point, whenever it makes sense for your application, you go back to the scars, and fix the tree. (Scaring can also have a more general meaning, which involves memorizing certain paths through<br \/>\nthe tree, so that you can make changes at the leave, then a few steps up, then back at the leaf. It&#8217;s a similar concept; in both forms of scarring, you&#8217;re optimizing to reduce the cost of zipping up and down the tree. )<\/p>\n<p> Either way, it gets a bit more complicated &#8211; and when you look at the code<br \/>\nfor a zipper, the re-balancing\/invariant fixing has a tendency to dominate the complexity<br \/>\nof the code. The zipper itself is so simple and so elegant that it just disappears under<br \/>\nthe weight of tree-balancing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Haskell stuff, I was planning on moving on to some monad-related stuff. But I had a reader write in, and ask me to write another post on data structures, focusing on a structured called a zipper. A zipper is a remarkably clever idea. It&#8217;s not really a single data structure, but rather a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[83,89],"tags":[],"class_list":["post-839","post","type-post","status-publish","format-standard","hentry","category-data-structures","category-haskell"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-dx","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=839"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/839\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=839"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}