|
|
|
@ -5,12 +5,6 @@ tags: ["Haskell"] |
|
|
|
|
draft: true |
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
<style> |
|
|
|
|
img, figure.small img { max-height: 20rem; } |
|
|
|
|
figure.tiny img { max-height: 15rem; } |
|
|
|
|
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! |
|
|
|
@ -132,7 +126,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: |
|
|
|
|
|
|
|
|
|
{{< figure src="length_1.png" caption="The initial graph of `length [1]`." >}} |
|
|
|
|
{{< figure src="length_1.png" caption="The initial graph of `length [1]`." class="small" >}} |
|
|
|
|
|
|
|
|
|
In this image, the `@` nodes represent function application. The |
|
|
|
|
root node is an application of the function `length` to the graph that |
|
|
|
@ -143,7 +137,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: |
|
|
|
|
|
|
|
|
|
{{< figure src="length_2.png" caption="The graph of `length [1]` after the body of `length` is expanded." >}} |
|
|
|
|
{{< figure src="length_2.png" caption="The graph of `length [1]` after the body of `length` is expanded." class="small" >}} |
|
|
|
|
|
|
|
|
|
Conceptually, we only took one reduction step, and thus, we haven't yet gotten |
|
|
|
|
to evaluating the recursive call to `length`. Since `(+)` |
|
|
|
@ -175,7 +169,7 @@ let x = square 5 in x + x |
|
|
|
|
|
|
|
|
|
Here, the initial graph looks as follows: |
|
|
|
|
|
|
|
|
|
{{< figure src="square_1.png" caption="The initial graph of `let x = square 5 in x + x`." >}} |
|
|
|
|
{{< figure src="square_1.png" caption="The initial graph of `let x = square 5 in x + x`." class="small" >}} |
|
|
|
|
|
|
|
|
|
As you can see, this _is_ a graph, but not a tree! Since both |
|
|
|
|
variables `x` refer to the same expression, `square 5`, they |
|
|
|
@ -183,7 +177,7 @@ 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: |
|
|
|
|
|
|
|
|
|
{{< figure src="square_2.png" caption="The graph of `let x = square 5 in x + x` after `square 5` is reduced." >}} |
|
|
|
|
{{< figure src="square_2.png" caption="The graph of `let x = square 5 in x + x` after `square 5` is reduced." class="small" >}} |
|
|
|
|
|
|
|
|
|
There are two `25`s in the graph, and no more `square`s! We only |
|
|
|
|
had to evaluate `square 5` exactly once, even though `(+)` |
|
|
|
@ -215,7 +209,7 @@ 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: |
|
|
|
|
|
|
|
|
|
{{< figure src="fixpoint_2.png" caption="The graph of `fix (1:)` after it's been reduced." >}} |
|
|
|
|
{{< figure src="fixpoint_2.png" caption="The graph of `fix (1:)` after it's been reduced." class="small" >}} |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
@ -254,11 +248,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: |
|
|
|
|
|
|
|
|
|
{{< figure src="notation.png" caption="The new visual notation used in this section." >}} |
|
|
|
|
{{< figure src="notation.png" caption="The new visual notation used in this section." class="small" >}} |
|
|
|
|
|
|
|
|
|
Now, let's write the initial graph for `doRepMax [1,2]`: |
|
|
|
|
|
|
|
|
|
{{< figure src="repmax_1.png" caption="The initial graph of `doRepMax [1,2]`." >}} |
|
|
|
|
{{< figure src="repmax_1.png" caption="The initial graph of `doRepMax [1,2]`." class="small" >}} |
|
|
|
|
|
|
|
|
|
Other than our new notation, there's nothing too surprising here. |
|
|
|
|
The first step of our hypothetical reduction would replace the application of `doRepMax` with its |
|
|
|
@ -268,7 +262,7 @@ the tuple, we apply `repMax` to the list `[1,2]` and the first element |
|
|
|
|
of its result. The list `[1,2]` itself |
|
|
|
|
consists of two uses of the `(:)` function. |
|
|
|
|
|
|
|
|
|
{{< figure src="repmax_2.png" caption="The first step of reducing `doRepMax [1,2]`." >}} |
|
|
|
|
{{< figure src="repmax_2.png" caption="The first step of reducing `doRepMax [1,2]`." class="small" >}} |
|
|
|
|
|
|
|
|
|
Next, we would also expand the body of `repMax`. In |
|
|
|
|
the following diagram, to avoid drawing a noisy amount of |
|
|
|
@ -372,7 +366,7 @@ 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. |
|
|
|
|
After removing the unused nodes, we are left with the following graph: |
|
|
|
|
|
|
|
|
|
{{< figure src="repmax_10.png" caption="The result of reducing `doRepMax [1,2]`." >}} |
|
|
|
|
{{< figure src="repmax_10.png" caption="The result of reducing `doRepMax [1,2]`." class="small" >}} |
|
|
|
|
|
|
|
|
|
As we would have expected, two `2`s were printed to the console, and our |
|
|
|
|
final graph represents the list `[2,2]`. |
|
|
|
|