Add images to time traveling post.
After Width:  Height:  Size: 28 KiB 
After Width:  Height:  Size: 53 KiB 

@ 5,6 +5,11 @@ tags: ["Haskell"]


draft: true







<style>


img, figure.small img { maxheight: 20rem; }


figure.medium img { maxheight: 30rem; }


</style>




I recently got to use a very curious Haskell technique


{{< sidenote "right" "productionnote" "in production:" >}}


As production as research code gets, anyway!



@ 126,7 +131,7 @@ length ((:) 1 [])


We're now ready to draw the graph; in this case, it's pretty much identical


to the syntax tree of the last form of our expression:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="length_1.png" caption="The initial graph of `length [1]`." >}}




In this image, the `@` nodes represent function application. The


root node is an application of the function `length` to the graph that



@ 137,7 +142,7 @@ list, and function applications in Haskell are


in the process of evaluation, the body of `length` will be reached,


and leave us with the following graph:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="length_2.png" caption="The graph of `length [1]` after the body of `length` is expanded." >}}




Conceptually, we only took one reduction step, and thus, we haven't yet gotten


to evaluating the recursive call to `length`. Since `(+)`



@ 169,15 +174,15 @@ let x = square 5 in x + x




Here, the initial graph looks as follows:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="square_1.png" caption="The initial graph of `let x = square 5 in x + x`." >}}




As you can see, this _is_ a graph, not a tree! Since both


As you can see, this _is_ a graph, but not a tree! Since both


variables `x` refer to the same expression, `square 5`, they


are represented by the same subgraph. Then, when we evaluate `square 5`


for the first time, and replace its root node with an indirection,


we end up with the following:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="square_2.png" caption="The graph of `let x = square 5 in x + x` after `square 5` is reduced." >}}




There are two `25`s in the tree, and no more `square`s! We only


had to evaluate `square 5` exactly once, even though `(+)`



@ 202,14 +207,14 @@ fix f = let x = f x in x


See how the definition of `x` refers to itself? This is what


it looks like in graph form:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="fixpoint_1.png" caption="The initial graph of `let x = f x in x`." >}}




I think it's useful to take a look at how this graph is processed. Let's


pick `f = (1:)`. That is, `f` is a function that takes a list,


and prepends `1` to it. Then, after constructing the graph of `f x`,


we end up with the following:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="fixpoint_2.png" caption="The graph of `fix (1:)` after it's been reduced." >}}




We see the body of `f`, which is the application of `(:)` first to the


constant `1`, and then to `f`'s argument (`x`, in this case). As



@ 247,11 +252,11 @@ of using application nodes `@`, let's draw an application of a


function `f` to arguments `x1` through `xn` as a subgraph with root `f`


and children `x`s. The below figure demonstrates what I mean:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="notation.png" caption="The new visual notation used in this section." >}}




Now, let's write the initial graph for `doRepMax [1,2]`:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_1.png" caption="The initial graph of `doRepMax [1,2]`." >}}




Other than our new notation, there's nothing too surprising here.


At a high level, all we want is the second element of the tuple



@ 263,7 +268,7 @@ The first step


of our hypothetical reduction would replace the application of `doRepMax` with its


body, and create our graph's first cycle:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_2.png" caption="The first step of reducing `doRepMax [1,2]`." >}}




Next, we would do the same for the body of `repMax`. In


the following diagram, to avoid drawing a noisy amount of



@ 273,7 +278,7 @@ edges to similar looking stars. This is merely


a visual trick; an edge leading to a little star is


actually an edge leading to `fst`. Take a look:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_3.png" caption="The second step of reducing `doRepMax [1,2]`." class="medium" >}}




Since `(,)` is a constructor, let's say that it doesn't


need to be evaluated, and that its



@ 289,9 +294,11 @@ thus replace the application of `snd` with an


indirection to this subgraph. This leaves us


with the following:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_4.png" caption="The third step of reducing `doRepMax [1,2]`." class="medium" >}}




If our original `doRepMax [1, 2]` expression occured at the top level,


Since it's becoming hard to keep track of what part of the graph


is actually being evaluated, I marked the former root of `doRepMax [1,2]` with


a blue star. If our original expression occured at the top level,


the graph reduction would probably stop here. After all,


we're evaluating our graphs using callbyneed, and there


doesn't seem to be a need for knowing the what the arguments of `(:)` are.



@ 307,7 +314,7 @@ of `fst`. We evaluate it in a similar manner to `snd`. That is,


we replace this `fst` with an indirection to the first element


of the argument tuple, which happens to be the subgraph starting with `max`:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_5.png" caption="The fourth step of reducing `doRepMax [1,2]`." class="medium" >}}




Phew! Next, we need to evaluate the body of `max`. Let's make one more


simplification here: rather than substitututing `max` for its body



@ 319,23 +326,23 @@ a call to `fst`, needs to be processed. To do so, we need to


evaluate the call to `repMax`. We thus replace `repMax`


with its body:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_6.png" caption="The fifth step of reducing `doRepMax [1,2]`." class="medium" >}}




We've reached one of the base cases here, and there


are no more calls to `max` or `repMax`. The whole reason


we're here is to evaluate the call to `fst` that's one


of the arguments to `max`. Given this graph, this is easy.


of the arguments to `max`. Given this graph, doing so is easy.


We can clearly see that `2` is the first element of the tuple


returned by `repMax [2]`. We thus replace `fst` with


an indirection to this node:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_7.png" caption="The sixth step of reducing `doRepMax [1,2]`." class="medium" >}}




This concludes our task of evaluating the arguments to `max`.


Comparing them, we see that `2` is greater than `1`, and thus,


we replace `max` with an indirection to `2`:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_8.png" caption="The seventh step of reducing `doRepMax [1,2]`." class="medium" >}}




The node that we starred in our graph is now an indirection (the


one that used to be the call to `fst`) which points to



@ 351,7 +358,7 @@ which is the call to `snd`. This `snd` is applied to an instance of `(,)`, which


can't be reduced any further. Thus, all we have to do is take the second


element of the tuple, and replace `snd` with an indirection to it:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_9.png" caption="The eighth step of reducing `doRepMax [1,2]`." class="medium" >}}




The second element of the tuple was a call to `(:)`, and that's what the mysterious


force is processing now. Just like it did before, it starts by looking at the



@ 362,10 +369,10 @@ Another `2` pops up on the console.


Finally, the mysterious force reaches the second argument of the `(:)`,


which is the empty list. The empty list also cannot be evaluated any


further, so that's what the mysterious force receives. Just like that,


there's nothing left to print to the console. The mysterious force ceases,


and we're left with the following graph:


there's nothing left to print to the console. The mysterious force ceases.


After removing the unused nodes, we are left with the following graph:




{{< todo >}}Add image!{{< /todo >}}


{{< figure src="repmax_10.png" caption="The result of reducing `doRepMax [1,2]`." >}}




As we would have expected, two `2`s are printed to the console.



After Width:  Height:  Size: 54 KiB 
After Width:  Height:  Size: 74 KiB 
After Width:  Height:  Size: 99 KiB 
After Width:  Height:  Size: 44 KiB 
After Width:  Height:  Size: 53 KiB 
After Width:  Height:  Size: 70 KiB 
After Width:  Height:  Size: 130 KiB 
After Width:  Height:  Size: 123 KiB 
After Width:  Height:  Size: 118 KiB 
After Width:  Height:  Size: 122 KiB 
After Width:  Height:  Size: 132 KiB 
After Width:  Height:  Size: 125 KiB 
After Width:  Height:  Size: 102 KiB 
After Width:  Height:  Size: 58 KiB 
After Width:  Height:  Size: 45 KiB 