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>
|
||||
<html><head><title>spout</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<meta content="spout" property="og:title" />
|
||||
<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 name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
|
|
@ -144,6 +144,12 @@ h2 {
|
|||
<body>
|
||||
<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">
|
||||
<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>
|
||||
|
|
@ -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!
|
||||
</p>
|
||||
<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='
|
||||
(() => {
|
||||
if (this.interval) {
|
||||
|
|
@ -398,7 +404,7 @@ let b = +batch.value;
|
|||
let [ins, tot] = [+inside.innerText, +total.innerText];
|
||||
if (!(n > 0)) {
|
||||
alert(`come on, ${n} samples?`);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
let ctx = canvas.getContext("2d");
|
||||
setTimeout(function sample() {
|
||||
|
|
@ -414,7 +420,7 @@ setTimeout(function sample() {
|
|||
|
||||
if (!n--) break;
|
||||
}
|
||||
|
||||
|
||||
inside.innerText = ins;
|
||||
total.innerText = 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.
|
||||
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 id="s9">
|
||||
<h1><a href="#s9">#</a> apropos of <a href="#s7">s7</a></h1>
|
||||
<p>i may not be stupid!
|
||||
|
|
@ -443,7 +449,7 @@ that might be here! in ! th efuture</p>
|
|||
this.innerText = "stop D:";
|
||||
let [lhs, rhs, canvas, samples] = elems(9, "lhs rhs canvas samples");
|
||||
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 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;
|
||||
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);
|
||||
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);
|
||||
|
|
@ -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");
|
||||
ctx.fillStyle = "#76a6e4";
|
||||
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 = [];
|
||||
for (let x = x0; x <= x1; x += (x1 - x0)/600) {
|
||||
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">
|
||||
<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.
|
||||
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.
|
||||
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>
|
||||
|
|
|
|||
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