Compare commits
No commits in common. "9da584ded4d5afd2cdba1d027c6646b412b520ce" and "18339d7e4da47b13f45d96dbfa2951e3ceeefb4e" have entirely different histories.
9da584ded4
...
18339d7e4d
|
@ -1,95 +0,0 @@
|
||||||
---
|
|
||||||
title: "Clairvoyance for Good: Using Lazy Evaluation in Haskell"
|
|
||||||
date: 2020-05-03T20:05:29-07:00
|
|
||||||
tags: ["Haskell"]
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
||||||
While tackling a project for work, I ran across a rather unpleasant problem.
|
|
||||||
I don't think it's valuable to go into the specifics here (it's rather
|
|
||||||
large and convoluted); however, the outcome of this experience led me to
|
|
||||||
discover a very interesting technique for lazy functional languages,
|
|
||||||
and I want to share what I learned.
|
|
||||||
|
|
||||||
### Time Traveling
|
|
||||||
Some time ago, I read [this post](https://kcsongor.github.io/time-travel-in-haskell-for-dummies/) by Csongor Kiss about time traveling in Haskell. It's
|
|
||||||
really cool, and makes a lot of sense if you have wrapped your head around
|
|
||||||
lazy evaluation. I'm going to present my take on it here, but please check out
|
|
||||||
Csongor's original post if you are interested.
|
|
||||||
|
|
||||||
Say that you have a list of integers, like `[3,2,6]`. Next, suppose that
|
|
||||||
you want to find the maximum value in the list. You can implement such
|
|
||||||
behavior quite simply with pattern matching:
|
|
||||||
|
|
||||||
```Haskell
|
|
||||||
myMax :: [Int] -> Int
|
|
||||||
myMax [] = error "Being total sucks"
|
|
||||||
myMax (x:xs) = max x $ myMax xs
|
|
||||||
```
|
|
||||||
|
|
||||||
You could even get fancy with a `fold`:
|
|
||||||
|
|
||||||
```Haskell
|
|
||||||
myMax :: [Int] -> Int
|
|
||||||
myMax = foldr1 max
|
|
||||||
```
|
|
||||||
|
|
||||||
All is well, and this is rather elementary Haskell. But now let's look at
|
|
||||||
something that Csongor calls the `repMax` problem:
|
|
||||||
|
|
||||||
> Imagine you had a list, and you wanted to replace all the elements of the
|
|
||||||
> list with the largest element, by only passing the list once.
|
|
||||||
|
|
||||||
How can we possibly do this in one pass? First, we need to find the maximum
|
|
||||||
element, and only then can we have something to replace the other numbers
|
|
||||||
with! It turns out, though, that we can just expect to have the future
|
|
||||||
value, and all will be well. Csongor provides the following example:
|
|
||||||
|
|
||||||
```Haskell {linenos=table}
|
|
||||||
repMax :: [Int] -> Int -> (Int, [Int])
|
|
||||||
repMax [] rep = (rep, [])
|
|
||||||
repMax [x] rep = (x, [rep])
|
|
||||||
repMax (l : ls) rep = (m', rep : ls')
|
|
||||||
where (m, ls') = repMax ls rep
|
|
||||||
m' = max m l
|
|
||||||
|
|
||||||
doRepMax :: [Int] -> [Int]
|
|
||||||
doRepMax xs = xs'
|
|
||||||
where (largest, xs') = repMax xs largest
|
|
||||||
```
|
|
||||||
|
|
||||||
In the above snippet, `repMax` expects to receive the maximum value of
|
|
||||||
its input list. At the same time, it also computes that maximum value,
|
|
||||||
returning it and the newly created list. `doRepMax` is where the magic happens:
|
|
||||||
the `where` clauses receives the maximum number from `repMax`, while at the
|
|
||||||
same time using that maximum number to call `repMax`!
|
|
||||||
|
|
||||||
This works because Haskell's evaluation model is, effectively,
|
|
||||||
[lazy graph reduction](https://en.wikipedia.org/wiki/Graph_reduction). That is,
|
|
||||||
you can think of Haskell as manipulating your code as
|
|
||||||
{{< sidenote "right" "tree-note" "a syntax tree," >}}
|
|
||||||
Why is it called graph reduction, you may be wondering, if the runtime is
|
|
||||||
manipulating syntax trees? To save on work, if a program refers to the
|
|
||||||
same value twice, Haskell has both of those references point to the
|
|
||||||
exact same graph. This violates the tree's property of having only one path
|
|
||||||
from the root to any node, and makes our program a graph. Graphs that
|
|
||||||
refer to themselves also violate the properties of a tree.
|
|
||||||
{{< /sidenote >}} performing
|
|
||||||
substitutions and simplifications as necessary until it reaches a final answer.
|
|
||||||
What the lazy part means is that parts of the syntax tree that are not yet
|
|
||||||
needed to compute the final answer can exist, unsimplied, in the tree. This is
|
|
||||||
what allows us to write the code above: the graph of `repMax xs largest`
|
|
||||||
effectively refers to itself. While traversing the list, it places references
|
|
||||||
to itself in place of each of the elements, and thanks to laziness, these
|
|
||||||
references are not evaluated.
|
|
||||||
|
|
||||||
Let's try a more complicated example. How about instead of creating a new list,
|
|
||||||
we return a `Map` containing the number of times each number occured, but only
|
|
||||||
when those numbers were a factor of the maximum numbers. Our expected output
|
|
||||||
will be:
|
|
||||||
|
|
||||||
```
|
|
||||||
>>> countMaxFactors [1,3,3,9]
|
|
||||||
|
|
||||||
fromList [(1, 1), (3, 2), (9, 1)]
|
|
||||||
```
|
|
|
@ -40,7 +40,7 @@ $sidenote-highlight-border-width: .2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenote-label {
|
.sidenote-label {
|
||||||
border-bottom: .2rem dashed $primary-color;
|
border-bottom: .2rem solid $primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenote-checkbox {
|
.sidenote-checkbox {
|
||||||
|
|
|
@ -14,10 +14,10 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
font-family: $font-heading;
|
font-family: $font-heading;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
border-bottom: none;
|
color: black;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
|
@ -37,10 +37,6 @@ pre code {
|
||||||
background-color: $code-color;
|
background-color: $code-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.highlight table pre {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -73,33 +69,28 @@ div.highlight table pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
|
background-color: $primary-color;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0rem 0rem 1rem 0rem;
|
margin: 1rem 0rem 1rem 0rem;
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
nav a {
|
||||||
padding: 0.25rem 0.75rem 0.25rem .75rem;
|
padding: .75em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: white;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-bottom: none;
|
|
||||||
transition: color .25s;
|
transition: color .25s, background-color .25s;
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
|
background-color: white;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-subscript {
|
.post-subscript {
|
||||||
color: #8f8f8f;
|
color: #8f8f8f;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content {
|
.post-content {
|
||||||
|
@ -131,9 +122,8 @@ h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: black;
|
color: $primary-color-dark;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: .2rem solid $primary-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
@ -164,18 +154,6 @@ td {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr.header-divider {
|
|
||||||
background-color: $primary-color;
|
|
||||||
height: 0.3rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.page-list a {
|
|
||||||
border-bottom: none;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.katex-html {
|
.katex-html {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
{{- partial "head.html" . -}}
|
{{- partial "head.html" . -}}
|
||||||
<body>
|
<body>
|
||||||
{{- partial "header.html" . -}}
|
{{- partial "header.html" . -}}
|
||||||
<div class="container"><hr class="header-divider"></div>
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
{{- block "main" . }}{{- end }}
|
{{- block "main" . }}{{- end }}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<h2>{{ .Title }}</h2>
|
<h2>{{ .Title }}</h2>
|
||||||
|
|
||||||
<ul class="page-list">
|
<ul>
|
||||||
{{ range .Pages.ByDate.Reverse }}
|
{{ range .Pages.ByDate.Reverse }}
|
||||||
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<h2>{{ .Title }}</h2>
|
<h2>{{ .Title }}</h2>
|
||||||
<div class="post-subscript">
|
<div class="post-subscript">
|
||||||
<p>
|
<p>Posted on {{ .Date.Format "January 2, 2006" }}.</p>
|
||||||
|
<p>Tags:
|
||||||
{{ range .Params.tags }}
|
{{ range .Params.tags }}
|
||||||
<a class="button" href="{{ $.Site.BaseURL }}/tags/{{ . | urlize }}">{{ . }}</a>
|
<a class="button" href="{{ $.Site.BaseURL }}/tags/{{ . | urlize }}">{{ . }}</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</p>
|
</p>
|
||||||
<p>Posted on {{ .Date.Format "January 2, 2006" }}.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
|
|
||||||
Recent posts:
|
Recent posts:
|
||||||
<ul class="page-list">
|
<ul>
|
||||||
{{ range first 10 (where (where .Site.Pages.ByDate.Reverse "Section" "blog") ".Kind" "!=" "section") }}
|
{{ range first 10 (where (where .Site.Pages.ByDate.Reverse "Section" "blog") ".Kind" "!=" "section") }}
|
||||||
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="theme-color" content="#1dc868" />
|
<meta name="theme-color" content="#1dc868" />
|
||||||
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inconsolata&family=Raleway&family=Lora&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Lora|Raleway" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
|
||||||
{{ $style := resources.Get "scss/style.scss" | resources.ToCSS | resources.Minify }}
|
{{ $style := resources.Get "scss/style.scss" | resources.ToCSS | resources.Minify }}
|
||||||
{{ $sidenotes := resources.Get "scss/sidenotes.scss" | resources.ToCSS | resources.Minify }}
|
{{ $sidenotes := resources.Get "scss/sidenotes.scss" | resources.ToCSS | resources.Minify }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<h2>Tagged "{{ .Title }}"</h2>
|
<h2>Tagged "{{ .Title }}"</h2>
|
||||||
|
|
||||||
<ul class="page-list">
|
<ul>
|
||||||
{{ range .Pages.ByDate.Reverse }}
|
{{ range .Pages.ByDate.Reverse }}
|
||||||
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<h2>{{ .Title }}</h2>
|
<h2>{{ .Title }}</h2>
|
||||||
Below is a list of all the tags ever used on this site.
|
Below is a list of all the tags ever used on this site.
|
||||||
|
|
||||||
<ul class="page-list">
|
<ul>
|
||||||
{{ range sort .Pages "Title" }}
|
{{ range sort .Pages "Title" }}
|
||||||
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
<li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user