diff --git a/content/blog/haskell_lazy_evaluation/fixpoint_1.png b/content/blog/haskell_lazy_evaluation/fixpoint_1.png new file mode 100644 index 0000000..6963e81 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/fixpoint_1.png differ diff --git a/content/blog/haskell_lazy_evaluation/fixpoint_2.png b/content/blog/haskell_lazy_evaluation/fixpoint_2.png new file mode 100644 index 0000000..1868f2b Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/fixpoint_2.png differ diff --git a/content/blog/haskell_lazy_evaluation.md b/content/blog/haskell_lazy_evaluation/index.md similarity index 87% rename from content/blog/haskell_lazy_evaluation.md rename to content/blog/haskell_lazy_evaluation/index.md index 0f56750..e760a53 100644 --- a/content/blog/haskell_lazy_evaluation.md +++ b/content/blog/haskell_lazy_evaluation/index.md @@ -5,6 +5,11 @@ tags: ["Haskell"] draft: true --- + + 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. diff --git a/content/blog/haskell_lazy_evaluation/length_1.png b/content/blog/haskell_lazy_evaluation/length_1.png new file mode 100644 index 0000000..a358954 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/length_1.png differ diff --git a/content/blog/haskell_lazy_evaluation/length_2.png b/content/blog/haskell_lazy_evaluation/length_2.png new file mode 100644 index 0000000..b269141 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/length_2.png differ diff --git a/content/blog/haskell_lazy_evaluation/notation.png b/content/blog/haskell_lazy_evaluation/notation.png new file mode 100644 index 0000000..9a62858 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/notation.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_1.png b/content/blog/haskell_lazy_evaluation/repmax_1.png new file mode 100644 index 0000000..82ee3eb Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_1.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_10.png b/content/blog/haskell_lazy_evaluation/repmax_10.png new file mode 100644 index 0000000..7a2bc18 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_10.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_2.png b/content/blog/haskell_lazy_evaluation/repmax_2.png new file mode 100644 index 0000000..d5df722 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_2.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_3.png b/content/blog/haskell_lazy_evaluation/repmax_3.png new file mode 100644 index 0000000..e98d97e Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_3.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_4.png b/content/blog/haskell_lazy_evaluation/repmax_4.png new file mode 100644 index 0000000..6fd33f7 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_4.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_5.png b/content/blog/haskell_lazy_evaluation/repmax_5.png new file mode 100644 index 0000000..94b2689 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_5.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_6.png b/content/blog/haskell_lazy_evaluation/repmax_6.png new file mode 100644 index 0000000..5d13dc3 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_6.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_7.png b/content/blog/haskell_lazy_evaluation/repmax_7.png new file mode 100644 index 0000000..142a296 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_7.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_8.png b/content/blog/haskell_lazy_evaluation/repmax_8.png new file mode 100644 index 0000000..01f490d Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_8.png differ diff --git a/content/blog/haskell_lazy_evaluation/repmax_9.png b/content/blog/haskell_lazy_evaluation/repmax_9.png new file mode 100644 index 0000000..505b362 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/repmax_9.png differ diff --git a/content/blog/haskell_lazy_evaluation/square_1.png b/content/blog/haskell_lazy_evaluation/square_1.png new file mode 100644 index 0000000..37f35e4 Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/square_1.png differ diff --git a/content/blog/haskell_lazy_evaluation/square_2.png b/content/blog/haskell_lazy_evaluation/square_2.png new file mode 100644 index 0000000..d64274a Binary files /dev/null and b/content/blog/haskell_lazy_evaluation/square_2.png differ