Add images to time traveling post.

This commit is contained in:
Danila Fedorin 2020-07-29 20:09:32 -07:00
parent d64a0d1fcd
commit 3aa2a6783e
18 changed files with 29 additions and 22 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -5,6 +5,11 @@ tags: ["Haskell"]
draft: true
---
<style>
img, figure.small img { max-height: 20rem; }
figure.medium img { max-height: 30rem; }
</style>
I recently got to use a very curious Haskell technique
{{< sidenote "right" "production-note" "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 call-by-need, 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB