spout: s17
This commit is contained in:
parent
2748bc5640
commit
f932215a0d
3 changed files with 66 additions and 68 deletions
|
|
@ -1,58 +0,0 @@
|
||||||
---
|
|
||||||
title: "Monads: Why and how"
|
|
||||||
description: I could not repress the urge to make a monad explainer any longer. Did you know that a monad is a monoid in the category of endofunctors?
|
|
||||||
tags: post,short,haskell
|
|
||||||
date: 2026-05-06 01:20:10 -5
|
|
||||||
---
|
|
||||||
|
|
||||||
I acknowledge that [the world wide web has one hundred trillion monad explainers.](https://wiki.haskell.org/Monad_tutorials_timeline)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Why monads
|
|
||||||
In the land of Haskell and friends, we love referential transparency.
|
|
||||||
We greatly appreciate that
|
|
||||||
```haskell
|
|
||||||
let x = 1 in x + x
|
|
||||||
```
|
|
||||||
will always have the same result as
|
|
||||||
```haskell
|
|
||||||
1 + 1
|
|
||||||
```
|
|
||||||
|
|
||||||
In languages where (almost) no functions have unmarked side-effects, like Haskell, you can even be confident that
|
|
||||||
```haskell
|
|
||||||
let x = f y in x + x
|
|
||||||
```
|
|
||||||
is the same as
|
|
||||||
```haskell
|
|
||||||
(f y) + (f y)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a truly excellent property to have, but it causes some problems when your program starts interacting with the world.
|
|
||||||
|
|
||||||
Aside from referential transparency, we don't want to fix evaluation order if we don't have to;
|
|
||||||
giving the compiler more freedom to reorder evaluation lets it perform more optimizations.
|
|
||||||
And we always want more optimizations.
|
|
||||||
|
|
||||||
But, quite often, we *need* things to happen in a specific order.
|
|
||||||
|
|
||||||
Monads let us do two things:
|
|
||||||
1. Order operations
|
|
||||||
2. Use past results to decide what to do next
|
|
||||||
|
|
||||||
## How monads
|
|
||||||
Easy:
|
|
||||||
```haskell
|
|
||||||
class Monad m where
|
|
||||||
(>>=) :: m a -> (a -> m b) -> m b
|
|
||||||
pure :: a -> m a
|
|
||||||
```
|
|
||||||
|
|
||||||
`>>=`, pronounced `bind`, gives us ordering and lets us use past results to decide what to do next.
|
|
||||||
|
|
||||||
The fact that it gives us ordering is made more clear if we define `>>`, which ignores its result:
|
|
||||||
```haskell
|
|
||||||
(>>) :: m a -> m b -> m b
|
|
||||||
a >> b = a >>= \_ -> b
|
|
||||||
```
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html><head><title>spout</title>
|
<html><head><title>spout</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta content="spout" property="og:title" />
|
<meta content="spout" property="og:title" />
|
||||||
<meta content="i post html" property="og:description" />
|
<meta content="i post html" property="og:description" />
|
||||||
<meta content="https://terezi.pyrope.net/spout" property="og:url" />
|
<meta content="https://terezi.pyrope.net/spout" property="og:url" />
|
||||||
<meta content="#008282" data-react-helmet="true" name="theme-color" />
|
<meta content="#008282" data-react-helmet="true" name="theme-color" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -144,6 +144,12 @@ h2 {
|
||||||
<body>
|
<body>
|
||||||
<h1 style="margin: 1rem">spout: reverse-chron html pieces by <a href="https://terezi.pyrope.net">mehbark</a></h1>
|
<h1 style="margin: 1rem">spout: reverse-chron html pieces by <a href="https://terezi.pyrope.net">mehbark</a></h1>
|
||||||
|
|
||||||
|
<article id="s18">
|
||||||
|
<h1><a href="#s18">#</a> variable weight noodling</h1>
|
||||||
|
<p>warning: smoothly changing fonts that may be a bit nauseating. also uses google fonts (shame).</p>
|
||||||
|
<p><a href="./variable-font-noodling.html">enter if you dare</a></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
<article id="s17">
|
<article id="s17">
|
||||||
<h1><a href="#s17">#</a> graphing with sign-changes</h1>
|
<h1><a href="#s17">#</a> graphing with sign-changes</h1>
|
||||||
<p>I had somehow never heard of this canonical graphing technique until watching <a href="https://www.youtube.com/watch?v=YJJk0iLJ4Ks">this video about a 3d graphing calculator in minecraft</a></p>
|
<p>I had somehow never heard of this canonical graphing technique until watching <a href="https://www.youtube.com/watch?v=YJJk0iLJ4Ks">this video about a 3d graphing calculator in minecraft</a></p>
|
||||||
|
|
@ -297,7 +303,7 @@ this version is unproven, <a href="https://www.gavalas.dev/blog/spigot-algorithm
|
||||||
fair warning: especially with larger batch sizes, this is liable to freeze up the tab. tread carefully!
|
fair warning: especially with larger batch sizes, this is liable to freeze up the tab. tread carefully!
|
||||||
</p>
|
</p>
|
||||||
<pre id=s12-output style="overflow: auto; height: 36ch; width: 100%; word-break: break-all; white-space: pre-wrap;">
|
<pre id=s12-output style="overflow: auto; height: 36ch; width: 100%; word-break: break-all; white-space: pre-wrap;">
|
||||||
3.</pre>
|
3.</pre>
|
||||||
<button onclick='
|
<button onclick='
|
||||||
(() => {
|
(() => {
|
||||||
if (this.interval) {
|
if (this.interval) {
|
||||||
|
|
@ -398,7 +404,7 @@ let b = +batch.value;
|
||||||
let [ins, tot] = [+inside.innerText, +total.innerText];
|
let [ins, tot] = [+inside.innerText, +total.innerText];
|
||||||
if (!(n > 0)) {
|
if (!(n > 0)) {
|
||||||
alert(`come on, ${n} samples?`);
|
alert(`come on, ${n} samples?`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ctx = canvas.getContext("2d");
|
let ctx = canvas.getContext("2d");
|
||||||
setTimeout(function sample() {
|
setTimeout(function sample() {
|
||||||
|
|
@ -414,7 +420,7 @@ setTimeout(function sample() {
|
||||||
|
|
||||||
if (!n--) break;
|
if (!n--) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
inside.innerText = ins;
|
inside.innerText = ins;
|
||||||
total.innerText = tot;
|
total.innerText = tot;
|
||||||
best.innerText = 4 * ins/tot;
|
best.innerText = 4 * ins/tot;
|
||||||
|
|
@ -426,7 +432,7 @@ setTimeout(function sample() {
|
||||||
<p>this is thoroughly ingrained in my brain, but i hope it's still fairly intuitive for others.
|
<p>this is thoroughly ingrained in my brain, but i hope it's still fairly intuitive for others.
|
||||||
i like how the boundary becomes more clear with more samples, it makes the accuracy more understandable <abbr title="in my opinion">imo</abbr>
|
i like how the boundary becomes more clear with more samples, it makes the accuracy more understandable <abbr title="in my opinion">imo</abbr>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article id="s9">
|
<article id="s9">
|
||||||
<h1><a href="#s9">#</a> apropos of <a href="#s7">s7</a></h1>
|
<h1><a href="#s9">#</a> apropos of <a href="#s7">s7</a></h1>
|
||||||
<p>i may not be stupid!
|
<p>i may not be stupid!
|
||||||
|
|
@ -443,7 +449,7 @@ that might be here! in ! th efuture</p>
|
||||||
this.innerText = "stop D:";
|
this.innerText = "stop D:";
|
||||||
let [lhs, rhs, canvas, samples] = elems(9, "lhs rhs canvas samples");
|
let [lhs, rhs, canvas, samples] = elems(9, "lhs rhs canvas samples");
|
||||||
let ctx = canvas.getContext("2d");
|
let ctx = canvas.getContext("2d");
|
||||||
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
||||||
let f = eval(`(x, y) => {with (Math) {return abs((${lhs.value}) - (${rhs.value}))}}`);
|
let f = eval(`(x, y) => {with (Math) {return abs((${lhs.value}) - (${rhs.value}))}}`);
|
||||||
|
|
||||||
let guys = [];
|
let guys = [];
|
||||||
|
|
@ -609,7 +615,7 @@ the first time was on <code>about:blank</code> in canvas, and the second time wa
|
||||||
let order = +document.getElementById("s7-deriv").innerText;
|
let order = +document.getElementById("s7-deriv").innerText;
|
||||||
for (let i = 0; i < order; i++) f = deriv(f);
|
for (let i = 0; i < order; i++) f = deriv(f);
|
||||||
|
|
||||||
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
||||||
ctx.clearRect(0, 0, 1000, 1000);
|
ctx.clearRect(0, 0, 1000, 1000);
|
||||||
for (let x = x0; x <= x1; x += (x1 - x0)/1200) {
|
for (let x = x0; x <= x1; x += (x1 - x0)/1200) {
|
||||||
ctx.fillRect(x.map(x0, x1, 0, 300), f(x).map(y0, y1, 300, 0), 1, 1);
|
ctx.fillRect(x.map(x0, x1, 0, 300), f(x).map(y0, y1, 300, 0), 1, 1);
|
||||||
|
|
@ -633,7 +639,7 @@ the first time was on <code>about:blank</code> in canvas, and the second time wa
|
||||||
let ctx = document.getElementById("s6-canvas").getContext("2d");
|
let ctx = document.getElementById("s6-canvas").getContext("2d");
|
||||||
ctx.fillStyle = "#76a6e4";
|
ctx.fillStyle = "#76a6e4";
|
||||||
let f = eval(`(x, y) => {with (Math) {return y - (${this.value})}}`);
|
let f = eval(`(x, y) => {with (Math) {return y - (${this.value})}}`);
|
||||||
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
|
||||||
let guys = [];
|
let guys = [];
|
||||||
for (let x = x0; x <= x1; x += (x1 - x0)/600) {
|
for (let x = x0; x <= x1; x += (x1 - x0)/600) {
|
||||||
guys.push({x, y: 0, dy: 0.1, lastDiff: 1});
|
guys.push({x, y: 0, dy: 0.1, lastDiff: 1});
|
||||||
|
|
@ -679,7 +685,7 @@ the first time was on <code>about:blank</code> in canvas, and the second time wa
|
||||||
<article id="s5">
|
<article id="s5">
|
||||||
<h1><a href="#s5">#</a> why spout</h1>
|
<h1><a href="#s5">#</a> why spout</h1>
|
||||||
<p>i have <a href="https://b.pyrope.net">my blog</a> for when i want to make really legit-looking posts.
|
<p>i have <a href="https://b.pyrope.net">my blog</a> for when i want to make really legit-looking posts.
|
||||||
it's made with <a href="https://docs.racket-lang.org/pollen/">pollen</a>, which is great, and i can make even the most lame posts (currently all of the posts) look very nice.
|
it's made with <a href="https://docs.racket-lang.org/pollen/">pollen</a>, which is great, and i can make even the most lame posts (currently all of the posts) look very nice.
|
||||||
however, there's a whole build system, the book is a program after all, a lot of things have to happen to make a post there happen. so i wanted something smaller. micro, if you will.
|
however, there's a whole build system, the book is a program after all, a lot of things have to happen to make a post there happen. so i wanted something smaller. micro, if you will.
|
||||||
hence, spout! i just write some html (which, tbh, i kinda prefer to markdown) with no restrictions. ssh, edit file, save file, done. it's great! it <em>really</em> needs an RSS feed, though…
|
hence, spout! i just write some html (which, tbh, i kinda prefer to markdown) with no restrictions. ssh, edit file, save file, done. it's great! it <em>really</em> needs an RSS feed, though…
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
50
site-src/spout/variable-font-noodling.html
Normal file
50
site-src/spout/variable-font-noodling.html
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>variable font weights</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:wght@200..800&family=Google+Sans+Flex:opsz,wght@6..144,1..1000&family=Inter:opsz,wght@14..32,100..900&family=JetBrains+Mono:wght@100..800&family=Montserrat:wght@100..900&family=Open+Sans:wght@300..800&family=Playfair+Display:wght@400..900&family=Source+Code+Pro:wght@200..900&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
list-decoration: none;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-size: 10rem;
|
||||||
|
text-align: center;
|
||||||
|
animation: vary 6.7s linear infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes vary {
|
||||||
|
from {
|
||||||
|
font-weight: var(--min);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
font-weight: var(--max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => document.querySelectorAll("li").forEach(li => {
|
||||||
|
const family = li.style.fontFamily.replaceAll('"', "");
|
||||||
|
const min = li.style.getPropertyValue("--min");
|
||||||
|
const max = li.style.getPropertyValue("--max");
|
||||||
|
li.title = `${family} (${min}-${max})`
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li style="--min: 200; --max: 800; font-family: 'Atkinson Hyperlegible Next'">Variable</li>
|
||||||
|
<li style="--min: 1; --max: 1000; font-family: 'Google Sans Flex'">Variable</li>
|
||||||
|
<li style="--min: 300; --max: 800; font-family: 'Open Sans'">Variable</li>
|
||||||
|
<li style="--min: 100; --max: 900; font-family: 'Inter'">Variable</li>
|
||||||
|
<li style="--min: 100; --max: 900; font-family: 'Montserrat'">Variable</li>
|
||||||
|
<li style="--min: 400; --max: 900; font-family: 'Playfair Display'">Variable</li>
|
||||||
|
<li style="--min: 200; --max: 900; font-family: 'Source Code Pro'">Variable</li>
|
||||||
|
<li style="--min: 100; --max: 800; font-family: 'JetBrains Mono'">Variable</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue