<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Julian Zhou</title>
    <description>The latest articles on DEV Community by Julian Zhou (@jzhonx).</description>
    <link>https://dev.to/jzhonx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4089512%2Fb223b0eb-2f51-4347-aec9-1b9ead09143a.jpg</url>
      <title>DEV Community: Julian Zhou</title>
      <link>https://dev.to/jzhonx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jzhonx"/>
    <language>en</language>
    <item>
      <title>Understanding Zippers</title>
      <dc:creator>Julian Zhou</dc:creator>
      <pubDate>Sat, 22 Aug 2026 10:34:08 +0000</pubDate>
      <link>https://dev.to/jzhonx/understanding-zippers-4dpf</link>
      <guid>https://dev.to/jzhonx/understanding-zippers-4dpf</guid>
      <description>&lt;p&gt;When working with tree-structured data, we often need to navigate to a specific node and modify it. In imperative languages, this is usually straightforward thanks to mutable state and parent pointers. In functional languages, however, immutability makes this pattern less obvious.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore how to navigate and modify tree structures efficiently in functional languages using a technique called &lt;strong&gt;zippers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple JSON query language
&lt;/h2&gt;

&lt;p&gt;Suppose we are implementing a simple JSON query tool. The language allows us to access and modify values in a JSON object.&lt;/p&gt;

&lt;p&gt;The query language has four simple operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;set path = value&lt;/code&gt; — replace the value at a path.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get path&lt;/code&gt; — read the value at a path.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;query | query&lt;/code&gt; — run two queries from left to right.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;at path { query }&lt;/code&gt; — move the cursor to a path and run a query relative to that node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In an imperative setting, we might represent the JSON as a tree with parent pointers, making navigation (both downward and upward) trivial.&lt;/p&gt;

&lt;p&gt;In a functional language like Haskell, we generally avoid parent pointers because maintaining them correctly under immutability is difficult. Instead, we need a different approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Suppose we have the following JSON object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the following two queries that do the same thing but with different syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set .a.b.x = 42
| set .a.b.y = 43

at .a.b {
  set .x = 42
  | set .y = 43
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;at&lt;/code&gt; block, paths are relative to &lt;code&gt;.a.b&lt;/code&gt;. When the block finishes, the cursor returns to its previous&lt;br&gt;
position.&lt;/p&gt;

&lt;p&gt;We will use this example to compare two ways of implementing the update operations in Haskell, and how to use zippers to&lt;br&gt;
navigate and modify the tree data structure efficiently.&lt;/p&gt;
&lt;h2&gt;
  
  
  Navigating and Modifying Trees
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Persistent data structure
&lt;/h3&gt;

&lt;p&gt;In Haskell, data structures are typically immutable. To modify a node in a tree, we need to create a new tree that&lt;br&gt;
contains the modified node, while sharing the unchanged nodes with the original tree. This is known as a &lt;strong&gt;persistent&lt;br&gt;
data structure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We define a minimal tree type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)])])]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  First Approach: Root-based
&lt;/h3&gt;

&lt;p&gt;The first query &lt;code&gt;set .a.b.x = 42 | set .a.b.y = 43&lt;/code&gt; can be implemented by accessing the target node and modifying it, then&lt;br&gt;
accessing another target node through the modified root node and modifying it again.&lt;/p&gt;

&lt;p&gt;To access a node, we recursively follow the path from the root to the target node and recursively create new nodes along&lt;br&gt;
the way. The unchanged nodes are shared between the original tree and the new tree. The number of nodes that are&lt;br&gt;
modified by the &lt;code&gt;access&lt;/code&gt; function is &lt;code&gt;O(depth(node))&lt;/code&gt;, where &lt;code&gt;depth(node)&lt;/code&gt; is the depth of the target node in the tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;break&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;modifiedChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="n"&gt;ks&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
       &lt;span class="kr"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifiedChild&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s"&gt;"Invalid path to access"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the path is empty, we apply the modification function f to the current node. Otherwise, we find the child with&lt;br&gt;
key k, recurse into it with the remaining path, and rebuild the current node with the modified child. The cost is&lt;br&gt;
O(depth(node)) new nodes per modification.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example of &lt;code&gt;access&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Suppose we modify the "x" node to 42 with &lt;code&gt;access ["a", "b", "x"] (const $ Atom 42) root&lt;/code&gt;, the new tree and the original&lt;br&gt;
tree would look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwtxj7u0jp7lu4mtpfnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwtxj7u0jp7lu4mtpfnt.png" alt="The original and updated trees share the unchanged y node" width="392" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the diagram, the &lt;code&gt;tree_a&lt;/code&gt; stands for a &lt;code&gt;Tree&lt;/code&gt; node that is a child of the root node with key "a". The &lt;code&gt;tree_a'&lt;/code&gt; is a&lt;br&gt;
modified version of &lt;code&gt;tree_a&lt;/code&gt; with the modified child node "x". The &lt;code&gt;root'&lt;/code&gt; is a modified version of the original root&lt;br&gt;
node with modified nodes. So is &lt;code&gt;tree_b'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From the diagram, we can see that the modified node "x" is a new node with value 42, and its parent node "b" is also a&lt;br&gt;
new node that shares the unchanged child node "y" with the original tree.&lt;/p&gt;
&lt;h4&gt;
  
  
  Query execution
&lt;/h4&gt;

&lt;p&gt;The whole query can be translated to the following Haskell code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
  &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;set .a.b.x = 42&lt;/code&gt; is translated to &lt;code&gt;access ["a", "b", "x"] (const $ Atom 42)&lt;/code&gt;, which evaluates to a function that takes a&lt;br&gt;
tree and returns a new tree, and so on. The &lt;code&gt;|&lt;/code&gt; operator is translated to function composition, which is &lt;code&gt;.&lt;/code&gt; in Haskell.&lt;br&gt;
The two &lt;code&gt;access&lt;/code&gt; functions are composed together, and the resulting function is applied to the original tree &lt;code&gt;root&lt;/code&gt; to&lt;br&gt;
get the modified tree.&lt;/p&gt;

&lt;p&gt;If there are &lt;code&gt;N&lt;/code&gt; modifications in the query, the total number of nodes that are modified is &lt;code&gt;O(N * depth(tree))&lt;/code&gt;. For&lt;br&gt;
modifications that are close to each other, this can lead to a lot of redundant modifications. In our example, the "a"&lt;br&gt;
and "b" nodes are modified twice, which is inefficient.&lt;/p&gt;
&lt;h3&gt;
  
  
  Second approach: cursor-based
&lt;/h3&gt;

&lt;p&gt;The query &lt;code&gt;at .a.b { set .x = 42 | set .y = 43 }&lt;/code&gt; introduces a cursor that allows us to focus on a specific node in&lt;br&gt;
the tree and execute a query with the focused node as the root node. The &lt;code&gt;at&lt;/code&gt; query can be implemented by using&lt;br&gt;
a technique called &lt;strong&gt;Zippers&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Zippers
&lt;/h4&gt;

&lt;p&gt;Zippers are a powerful technique for navigating and modifying persistent data structures like trees. They allow us to go&lt;br&gt;
to a parent node or to a specific child node in a much more efficient way, without needing to always go back to the root&lt;br&gt;
node to access a node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;breadcrumbs&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Crumb&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Crumb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Crumb&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;holeKey&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;Zipper&lt;/code&gt; consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;currently focused tree node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;a list of &lt;strong&gt;breadcrumbs&lt;/strong&gt; that stores the path from the root to the current node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;Crumb&lt;/code&gt; looks like a &lt;code&gt;Tree&lt;/code&gt;, except that one &lt;code&gt;Tree&lt;/code&gt; has been removed; it is the node we most recently descended into. The &lt;code&gt;holeKey&lt;/code&gt; stores the key of the removed node, &lt;code&gt;before&lt;/code&gt; contains the preceding siblings, and &lt;code&gt;after&lt;/code&gt;&lt;br&gt;
contains the following siblings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;emptyZipper&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;emptyZipper&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a Zipper focusing on the root:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftpxgg5tkbpkn74655xy7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftpxgg5tkbpkn74655xy7.png" alt="A zipper focused on the root with an empty breadcrumb stack" width="352" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the diagram, the focus node has the same value as the original root node, and the breadcrumb stack is empty because we are at the root node.&lt;/p&gt;

&lt;h4&gt;
  
  
  Move down
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;goDown&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;goDown&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;break&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Crumb&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;goDown&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Cannot go to child '"&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s"&gt;"' of tree: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving downward creates a &lt;code&gt;Crumb&lt;/code&gt; from the parent node by extracting the target child, which becomes the new focus node.&lt;br&gt;
The &lt;code&gt;Crumb&lt;/code&gt; is then pushed onto the breadcrumb stack.&lt;/p&gt;

&lt;p&gt;We go down to "a", the Zipper would look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwo1i3qb1r7py755kwk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwo1i3qb1r7py755kwk7.png" alt="The zipper after moving down to tree_a" width="412" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the diagram, the &lt;code&gt;crumb_0&lt;/code&gt; is the top &lt;code&gt;Crumb&lt;/code&gt; in the breadcrumb stack. The &lt;code&gt;holeKey&lt;/code&gt; indicates that the "a" node is&lt;br&gt;
taken away from the root node, and the &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; fields are empty because there is no sibling of "a". The focus&lt;br&gt;
node is identical to the "a" node in the original tree.&lt;/p&gt;

&lt;p&gt;Then go down to "b": &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjtfbc9d721fq8w9682dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjtfbc9d721fq8w9682dz.png" alt="The zipper after moving down to tree_b" width="460" height="912"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The focus node is identical to the "b" in the original tree. The &lt;code&gt;crumb_1&lt;/code&gt; looks similar to &lt;code&gt;tree_a&lt;/code&gt;, but the "b" node&lt;br&gt;
is taken away and replaced with a hole, which is indicated by the &lt;code&gt;holeKey&lt;/code&gt; field. The &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; fields are&lt;br&gt;
empty.&lt;/p&gt;

&lt;p&gt;Now we go down to "x", the Zipper would look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa23z4gbrm1alojxr0nvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa23z4gbrm1alojxr0nvm.png" alt="The zipper after moving down to the x value" width="608" height="936"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The newly added &lt;code&gt;crumb_2&lt;/code&gt; indicates that the "x" node is taken away from the "b" node, and the "y" node is a sibling of&lt;br&gt;
"x", so it is stored in the &lt;code&gt;after&lt;/code&gt; field of the &lt;code&gt;Crumb&lt;/code&gt;. The focus node is identical to the "x" node in the original&lt;br&gt;
tree.&lt;/p&gt;
&lt;h4&gt;
  
  
  Focus modification
&lt;/h4&gt;

&lt;p&gt;Now we modify the value of "x" to 42. We just call the &lt;code&gt;modify&lt;/code&gt; function on the focus node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;modifyZipper&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;modifyZipper&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modifying the focus node does not change the breadcrumbs, nor does it return a new root node. So the time complexity of&lt;br&gt;
&lt;code&gt;modifyZipper&lt;/code&gt; is &lt;code&gt;O(1)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We modify the "x" node to 42. Now in the zipper, the focus node is a new node with value 42.&lt;/p&gt;
&lt;h4&gt;
  
  
  Move up
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;goUp&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;goUp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Crumb&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt;
&lt;span class="n"&gt;goUp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s"&gt;"Already at the top"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Moving upward is a reverse process of moving downward. It &lt;strong&gt;reassembles the tree&lt;/strong&gt; by filling the hole in the Crumb with&lt;br&gt;
the current focus node, popping the Crumb from the list of breadcrumbs, and making the reassembled tree the new focus&lt;br&gt;
node.&lt;/p&gt;

&lt;p&gt;Now we go up, the Zipper would look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0spzit65uzjjov3dyy4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0spzit65uzjjov3dyy4a.png" alt="The zipper after rebuilding tree_b with the modified x value" width="460" height="856"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above diagram, the new focus node is created by filling the hole in the crumb with the modified "x" node that&lt;br&gt;
has value 42. The new focus node shares the unchanged child node "y" with the original "b" node.&lt;/p&gt;
&lt;h4&gt;
  
  
  access with Zipper
&lt;/h4&gt;

&lt;p&gt;Unlike &lt;code&gt;access&lt;/code&gt; which returns a new root node, &lt;code&gt;accessZ&lt;/code&gt; goes to the target node, applies the function to the focus&lt;br&gt;
node, and then goes back to the same position in the tree. The number of nodes that are modified by &lt;code&gt;accessZ&lt;/code&gt; is&lt;br&gt;
&lt;code&gt;O(distance(node, cursor))&lt;/code&gt;, where &lt;code&gt;distance(node, cursor)&lt;/code&gt; is the depth of the target node from the current cursor&lt;br&gt;
node. If the target node is close to the cursor node, the number of modified nodes is a much smaller number than&lt;br&gt;
&lt;code&gt;O(depth(node))&lt;/code&gt;, which is the number of modified nodes by &lt;code&gt;access&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="n"&gt;ks&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goDown&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;goUp&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;accessZ&lt;/code&gt; function, we first check if the path is empty. If it is, we apply the modification function &lt;code&gt;f&lt;/code&gt; to the&lt;br&gt;
current Zipper. If the path is not empty, we go down to the child node with key &lt;code&gt;k&lt;/code&gt;, recursively call &lt;code&gt;accessZ&lt;/code&gt; on the&lt;br&gt;
child node with the remaining path &lt;code&gt;ks&lt;/code&gt;, and then go back up to the original position.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;(&amp;amp;)&lt;/code&gt; operator is a reverse function application operator, which allows us to write the operand before the function.&lt;br&gt;
It is already defined in &lt;code&gt;Data.Function&lt;/code&gt; in Haskell, but we define it here for completeness.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;at&lt;/code&gt; implementation
&lt;/h4&gt;

&lt;p&gt;We can implement the &lt;code&gt;at&lt;/code&gt; query by using &lt;code&gt;accessZ&lt;/code&gt; to navigate to the target node, apply the modification, and&lt;br&gt;
then go back to the root node. The &lt;code&gt;withCursor&lt;/code&gt; function takes a path to the target node, a modification function that&lt;br&gt;
takes a Zipper and returns a modified Zipper, and the original tree. It returns a new tree with the modifications&lt;br&gt;
applied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;withCursor&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
&lt;span class="n"&gt;withCursor&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emptyZipper&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;accessWCursor&lt;/code&gt; function is a helper function that works similarly to &lt;code&gt;access&lt;/code&gt;, which allows us to modify a tree&lt;br&gt;
node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;accessWCursor&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Zipper&lt;/span&gt;
&lt;span class="n"&gt;accessWCursor&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessZ&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifyZipper&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Query execution
&lt;/h4&gt;

&lt;p&gt;The second query &lt;code&gt;at .a.b { set .x = 42 | set .y = 43 }&lt;/code&gt; will be translated to the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;withCursor&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessWCursor&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;accessWCursor&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses &lt;code&gt;withCursor&lt;/code&gt; to navigate to the "b" node, then with "b" as the cursor, modifies the "x" node and the "y" node&lt;br&gt;
with &lt;code&gt;accessWCursor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If there are &lt;code&gt;N&lt;/code&gt; modifications whose average distance from the cursor is &lt;code&gt;m&lt;/code&gt;, the total number of rebuilt nodes is&lt;br&gt;
&lt;code&gt;O(N * m + depth(tree))&lt;/code&gt;. When &lt;code&gt;m&lt;/code&gt; is much smaller than &lt;code&gt;depth(tree)&lt;/code&gt;, this is more efficient than the root-based&lt;br&gt;
approach's &lt;code&gt;O(N * depth(tree))&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Comparing the two approaches
&lt;/h3&gt;

&lt;p&gt;Let's compare the two approaches in terms of the number of node allocations and time complexity, assuming that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The child lookups and parent reconstructions take &lt;code&gt;O(1)&lt;/code&gt; time&lt;/li&gt;
&lt;li&gt;The average walk distance from the cursor to the target node is &lt;code&gt;m&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Tree nodes rebuilt&lt;/th&gt;
&lt;th&gt;Time complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Root-based &lt;code&gt;access&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N * depth(tree))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N * depth(tree))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zippers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N * m + depth(tree))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(N * m + depth(tree))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  When to use Zippers
&lt;/h2&gt;

&lt;p&gt;Zippers are not universally better than root-based access. Their advantage depends on the access pattern.&lt;/p&gt;
&lt;h3&gt;
  
  
  High spatial locality
&lt;/h3&gt;

&lt;p&gt;When a query performs many modifications in the same region of the tree, zippers avoid redundant rebuilds of the path&lt;br&gt;
from the root. In one high-locality Haskell tree-traversal benchmark, &lt;a href="https://arxiv.org/abs/1908.10926" rel="noopener noreferrer"&gt;Performance Analysis of&lt;br&gt;
Zippers&lt;/a&gt; reported a speedup of up to 280% over its root-based implementation. The key factor is &lt;strong&gt;spatial locality&lt;/strong&gt;: the closer the edits are to each other (and to the cursor),&lt;br&gt;
the greater the benefit.&lt;/p&gt;

&lt;p&gt;When modifications are scattered across unrelated parts of the tree, the zipper must navigate up and back down for each&lt;br&gt;
one, and the overhead of creating crumbs on every step can make it &lt;strong&gt;slower&lt;/strong&gt; than simply calling &lt;code&gt;access&lt;/code&gt; from the root&lt;br&gt;
each time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Read-only access
&lt;/h3&gt;

&lt;p&gt;Zipper navigation still allocates wrappers, and moving upward reconstructs parent nodes even when no value is modified.&lt;br&gt;
For read-only lookups, this overhead is wasted. An alternative is to maintain a cache from paths to values alongside the&lt;br&gt;
tree. Modifications update both the tree (via a zipper) and the cache; reads consult the cache directly without any&lt;br&gt;
navigation.&lt;/p&gt;

&lt;p&gt;For example, consider a query that is focused on &lt;code&gt;.a.b&lt;/code&gt; but needs to read the value of its child &lt;code&gt;.x&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;at .a.b {
  set .y = get .x + 1
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The read can be resolved by computing the absolute path &lt;code&gt;["a", "b", "x"]&lt;/code&gt; and looking it up in a cache, avoiding&lt;br&gt;
zipper navigation entirely.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rule of thumb
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Preferred approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Many edits clustered in one subtree&lt;/td&gt;
&lt;td&gt;Zipper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edits scattered across the tree&lt;/td&gt;
&lt;td&gt;Root-based &lt;code&gt;access&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read-only lookups&lt;/td&gt;
&lt;td&gt;Cache lookup / direct path lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Complexity of Zipper implementation
&lt;/h2&gt;

&lt;p&gt;Zipper implementation can have a lot of boilerplate code, especially when the tree structure is complex. For example, if&lt;br&gt;
we have the following Value tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Atom&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;BinOp&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;
         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UnOp&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each type of node that can have children, we need to define a complicated &lt;code&gt;Crumb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;ValueCrumb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ListCrumb&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;MapCrumb&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;BinOpLeftCrumb&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;BinOpRightCrumb&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kt"&gt;Value&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UnOpCrumb&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will talk about how to reduce the boilerplate code in the next post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;From an imperative-programming point of view, zippers can feel natural as they let us navigate and modify a tree node&lt;br&gt;
"in place", as opposed to the root-based approach where we always have to create a new tree for each modification.&lt;br&gt;
Zippers work best when edits are clustered - with a cursor near the action, each modification costs only the distance&lt;br&gt;
from the cursor instead of the full depth of the tree.&lt;/p&gt;

&lt;p&gt;However, they are not a universal replacement for root-based access. Scattered edits gain little from a zipper, and&lt;br&gt;
read-only lookups are better served by a flat index. The right choice depends on the access pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learnyouahaskell.github.io/zippers.html" rel="noopener noreferrer"&gt;Learn You a Haskell for Great Good!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>haskell</category>
    </item>
  </channel>
</rss>
