diff options
| author | Collin Williams <96917990+bluedragon1221@users.noreply.github.com> | 2026-09-19 21:13:32 -0500 |
|---|---|---|
| committer | Collin Williams <96917990+bluedragon1221@users.noreply.github.com> | 2026-09-19 21:13:32 -0500 |
| commit | b6f7b9dda8cd2e4b1c5b006ad5866d3ef7e62310 (patch) | |
| tree | cea3af7c287c28fcaf379ec3700f0aa76223e244 | |
| parent | ffd6abb1403d85b61c80fc8d23a3a04df8895c20 (diff) | |
inserting link:css via javascript was a bad idea
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | _template.html | 20 | ||||
| -rw-r--r-- | article.css | 6 | ||||
| -rw-r--r-- | article.js | 93 | ||||
| -rw-r--r-- | devshell.html | 98 | ||||
| -rw-r--r-- | fib.html | 21 | ||||
| -rw-r--r-- | forgejno.html | 24 | ||||
| -rw-r--r-- | index.html | 10 | ||||
| -rw-r--r-- | net.html | 42 | ||||
| -rw-r--r-- | no_ai.html | 9 | ||||
| -rw-r--r-- | reversible.html | 253 | ||||
| -rw-r--r-- | reversible.rs | 158 | ||||
| -rw-r--r-- | soplt.html | 180 | ||||
| m--------- | static | 0 | ||||
| -rw-r--r-- | writing.html | 14 |
16 files changed, 391 insertions, 542 deletions
diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 22c81ad..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "static"] - path = static - url = https://git.collin.williamsfam.us.com/shared-assets diff --git a/README.md b/README.md deleted file mode 100644 index 3b95297..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# This is the code for my website -2 diff --git a/_template.html b/_template.html deleted file mode 100644 index 65cedc6..0000000 --- a/_template.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - - <title>Title</title> - <link rel="stylesheet" href="prose.css"> - <meta property="article:published_time" content="YYYY-MM-DD"> - <script src="article.js"></script> -</head> -<body> - <article> - <header></header> - - <p> - lorem ipsum - </p> - </article> -</body> -</html>
\ No newline at end of file diff --git a/article.css b/article.css index f3fc047..7df5836 100644 --- a/article.css +++ b/article.css @@ -39,12 +39,12 @@ article { header { display: flex; flex-direction: column; - gap: var(--sx); + gap: 1rem; .header-top { display: flex; align-items: flex-start; - gap: var(--sx); + gap: 1rem; } .header-logo-link { @@ -72,7 +72,7 @@ header { } time { - line-height: var(--lh); + line-height: 1.5; margin: 0; } diff --git a/article.js b/article.js deleted file mode 100644 index ecf2d40..0000000 --- a/article.js +++ /dev/null @@ -1,93 +0,0 @@ -function addMetadata() { - // Favicon - const favicon = document.createElement("link"); - favicon.rel = "icon"; - favicon.href = "static/Logo.svg"; - favicon.type = "image/svg+xml"; - document.head.appendChild(favicon); - - // UTF-8 (for special characters) - const charset = document.createElement("meta"); - charset.setAttribute("charset", "UTF-8"); - document.head.appendChild(charset); - - // viewport (for mobile) - const viewport = document.createElement("meta"); - viewport.setAttribute("name", "viewport"); - viewport.setAttribute("content", "width=device-width, initial-scale=1.0"); - document.head.appendChild(viewport); - - // css - const proseCss = document.createElement("link"); - proseCss.rel = "stylesheet"; - proseCss.href = "article.css"; - document.head.appendChild(proseCss); -} - -function addHeader() { - const header = document.querySelector("header"); - - const headerTop = document.createElement("div"); - headerTop.classList.add("header-top"); - - const logoLink = document.createElement("a"); - logoLink.href = "writing"; - logoLink.classList.add("header-logo-link"); - headerTop.appendChild(logoLink); - - const info = document.createElement("div"); - info.classList.add("header-info"); - const h1 = document.createElement("h1"); - h1.textContent = document.title; - info.appendChild(h1); - const time = document.createElement("time"); - const metaDate = document.querySelector('meta[property="article:published_time"]'); - const dateString = metaDate.getAttribute("content"); - const displayDate = (new Date(dateString)).toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - timeZone: "UTC" - }); - time.setAttribute("datetime", dateString); - time.textContent = `Published ${displayDate}`; - info.appendChild(time); - headerTop.appendChild(info); - header.appendChild(headerTop); - - const hr = document.createElement("hr"); - header.appendChild(hr); -} - -function addTOC() { - const tocDiv = document.getElementById("toc"); - if (!tocDiv) return; - - const tocHeading = document.createElement("h2"); - tocHeading.textContent = "Table of Contents"; - tocDiv.appendChild(tocHeading); - - const toc = document.createElement("ul"); - - document.querySelectorAll('article :is(h1, h2, h3, h4, h5, h6):not(header *, #toc *)').forEach(heading => { - const link = document.createElement("a"); - link.href = "#" + heading.id.toLowerCase(); - link.textContent = heading.textContent; - - const li = document.createElement("li"); - li.appendChild(link); - - toc.appendChild(li); - }); - - tocDiv.appendChild(toc); -} - -document.addEventListener("DOMContentLoaded", () => { - addMetadata(); - addHeader(); - addTOC(); - - const article = document.querySelector('article'); - article.classList.add('markdown-body'); -}) diff --git a/devshell.html b/devshell.html index 59b36af..899e862 100644 --- a/devshell.html +++ b/devshell.html @@ -1,32 +1,51 @@ <!DOCTYPE html> <html> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> + <title>A solution to slow nix-shells</title> - <meta property="article:published_time" content="2026-08-26"> - <script src="./article.js"></script> </head> <body> - <article> - <header></header> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>A solution to slow nix-shells</h1> + <time datetime="2026-08-26">Published Aug 8, 2026</time> + </div> + </div> + <hr> + </header> <p> - Tired of waiting for <code>nix-shell</code> to re-download <code>github:NixOS/nixpkgs</code> after you <code>nix-collect-garbage</code> (or just randomly sometimes)? + Tired of waiting for <code><kw>nix-shell</kw></code> to re-download <code><str>github:NixOS/nixpkgs</str></code> after you <code><kw>nix-collect-garbage</kw></code> (or just randomly sometimes)? Me too! That's why I went down this rabit hole of making an instant nix shell. </p> - <div id="toc"></div> + <h2>Table of Contents</h2> + <ul> + <li><a href="#intro">What's wrong with nix-shell?</a></li> + <li><a href="#existing-solutions">Existing solutions</a></li> + <li><a href="#devshell-packages">Devshell packages</a></li> + <li><a href="#thoughts">Thoughts</a></li> + </ul> + <h2 id="intro">What's wrong with nix-shell?</h2> <p> First, let's clarify what I mean by a nix shell. I'm simply referring to the idea of hiding the packages that you use for development behind a command, and managing those packages using nix. - Most people are familiar with this through the <code>nix-shell</code> or <code>nix shell</code> commands. + Most people are familiar with this through the <code><kw>nix-shell</kw></code> or <code><kw>nix</kw> <prop>shell</prop></code> commands. (For non-nix people, yes those are in fact two very different things) </p> <p> - My issue with how <code>nix shell</code> is that it has to redownload your packages if they're not in the nix store (one cause of this would be if you recently ran <code>nix-collect-garbage</code>), and it has to re-evaluate the correct environment variables and packages every time you launch the shell. - These two things together mean that every time you run <code>nix shell</code>, you'll be waiting before you can access those packages. + My issue with how <code><kw>nix</kw> <prop>shell</prop></code> is that it has to redownload your packages if they're not in the nix store (one cause of this would be if you recently ran <code><kw>nix-collect-garbage</kw></code>), and it has to re-evaluate the correct environment variables and packages every time you launch the shell. + These two things together mean that every time you run <code><kw>nix</kw> <prop>shell</prop></code>, you'll be waiting before you can access those packages. And, personally, I'd rather spend my time waiting for my code to compile, not waiting to have access to my compiler. </p> @@ -35,21 +54,21 @@ There's a lot of existing solutions to this problem, but none of them do exactly what I want. </p> <p> - I used to be a big fan of <a href="https://github.com/xzfc/cached-nix-shell"><code>cached-nix-shell</code></a>. + I used to be a big fan of <a href="https://github.com/xzfc/cached-nix-shell"><code><kw>cached-nix-shell</kw></code></a>. It's cool because it (as the name suggests) caches your nix shell, so the next time you start it up, the launch time is instant (it removes the second factor of waiting mentioned in the previous section). But for some reason it breaks for certain packages that rely on paths that might not exist anymore. - For example, I had issues with <code>rustc</code> not working when launched through <code>cached-nix-shell</code> if it was using a cached version. + For example, I had issues with <code><kw>rustc</kw></code> not working when launched through <code><kw>cached-nix-shell</kw></code> if it was using a cached version. </p> <p> - Another solution that I think too many people fall for is to just install the packages into your system <code>$PATH</code> with <code>environment.systemPackages</code>. + Another solution that I think too many people fall for is to just install the packages into your system <code>$PATH</code> with <code><prop>environment</prop>.<prop>systemPackages</prop></code>. One of the great things about NixOS is that you don't have to resort to this! - On Arch, if you install a package with <code>pacman</code>, it has to go into your system <code>$PATH</code>. + On Arch, if you install a package with <code><kw>pacman</kw></code>, it has to go into your system <code>$PATH</code>. But on NixOS, we don't have to bloat the system path with our developer dependencies. - This is <em>exactly why</em> <code>nix-shell</code> exists in the first place! + This is <em>exactly why</em> <code><kw>nix-shell</kw></code> exists in the first place! </p> <p> - The most hardcore-nix-nerd option here is to manually mark your nix-shell as a GC root. - This tells <code>nix-collect-garbage</code> to not remove the path or anything that it depends on, preserving your nix-shells through garbage collection. + The most hardcore-nix-nerd option here is to manually mark your devshell as a GC root. + This tells <code><kw>nix-collect-garbage</kw></code> to not remove the path or anything that it depends on, preserving your devshells through garbage collection. Maybe some day I'll write my own tool that wraps this functionality to provide GC-protected nix shells, but honestly that just sounds like too much work. (for more information about how Garbage Collection works on Nix, check out <a href="https://nixos.org/guides/nix-pills/11-garbage-collector">the Nix Pill about the Garbage Collector</a>) </p> @@ -64,34 +83,34 @@ In your NixOS config, using home-manager or <a href="https://github.com/feel-co/hjem">hjem</a> or whatever, just build a package that launches your shell but with some extra programs. </p> - <pre><code>{pkgs, ...}: let - rust-shell = let - rustProfile = pkgs.buildEnv { - name = "rust-profile"; - paths = with pkgs; [rustc cargo rust-analyzer]; + <pre><code>{<prop>pkgs</prop>, ...}: <kw>let</kw> + <prop>rust-shell</prop> = <kw>let</kw> + <prop>rustProfile</prop> = <prop>pkgs</prop>.<prop>buildEnv</prop> { + <prop>name</prop> = <str>"rust-profile"</str>; + <prop>paths</prop> = <kw>with</kw> <prop>pkgs</prop>; [<prop>rustc</prop> <prop>cargo</prop> <prop>rust-analyzer</prop> <prop>gcc</prop>]; }; - in - pkgs.writeShellScriptBin "rust-shell" '' - export PATH="${rustProfile}/bin:$PATH" + <kw>in</kw> + <prop>pkgs</prop>.<prop>writeShellScriptBin</prop> <str>"rust-shell"</str> <str>'' + export PATH="${<prop>rustProfile</prop>}/bin:$PATH" exec $SHELL - ''; + ''</str>; - py-shell = let - pythonProfile = pkgs.python314.withPackages (ps: - with ps; [ - numpy - matplotlib - pandas - jupyter - ipython + <prop>py-shell</prop> = <kw>let</kw> + <prop>pythonProfile</prop> = <prop>pkgs</prop>.<prop>python314</prop>.<prop>withPackages</prop> (<prop>ps</prop>: + <kw>with</kw> <prop>ps</prop>; [ + <prop>numpy</prop> + <prop>matplotlib</prop> + <prop>pandas</prop> + <prop>jupyter</prop> + <prop>ipython</prop> ]); - in - pkgs.writeShellScriptBin "py-shell" '' - export PATH="${pythonProfile}/bin:$PATH" + <kw>in</kw> + <prop>pkgs</prop>.<prop>writeShellScriptBin</prop> <str>"py-shell"</str> <str>'' + export PATH="${<prop>pythonProfile</prop>}/bin:$PATH" exec $SHELL - ''; -in { - packages = [rust-shell py-shell]; + ''</str>; +<kw>in</kw> { + <prop>packages</prop> = [<prop>rust-shell</prop> <prop>py-shell</prop>]; }</code></pre> <h2 id="thoughts">Thoughts</h2> @@ -121,6 +140,7 @@ in { In my examples, the Rust one isn't that bad, but the Python one pulls in hundreds of python dependencies into the system closure. This can make rebuilds longer (when the packages need to update) and take up more space on your computer. </p> + <hr> <p> Even with these trade-offs, I'm willing to pay the price to have instantaneous access to my developer dependencies! </p> @@ -1,13 +1,25 @@ <!DOCTYPE html> <html> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> + <title>I accidentally derived a formula for the Fibonacci Sequence</title> - <meta property="article:published_time" content="2026-06-11"> - <script src="article.js"></script> </head> <body> - <article> - <header></header> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>I accidentally derived a formula for the Fibonacci Sequence</h1> + <time datetime="2026-06-11">Published Jun 11, 2026</time> + </div> + </div> + <hr> + </header> <p>Last night my brother pitched me this math problem over dinner (which he later revealed came from <a href="https://www.youtube.com/watch?v=XeOTNEb-QsM">this video</a>). It goes something like this:</p> <blockquote> @@ -322,3 +334,4 @@ </article> </body> </html> + diff --git a/forgejno.html b/forgejno.html index 100c606..97ec15d 100644 --- a/forgejno.html +++ b/forgejno.html @@ -1,20 +1,30 @@ <!DOCTYPE html> <html> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> + <title>You don't need Forgejo</title> <meta property="article:published_time" content="2026-05-03"> <script src="article.js"></script> </head> <body> - <article> - <header></header> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>You don't need Forgejo</h1> + <time datetime="2026-05-03">Published May 3, 2026</time> + </div> + </div> + <hr> + </header> <p> - <a href="https://mitchellh.com/writing/ghostty-leaving-github">People</a> - <a href="https://vinyl-cache.org/organization/moving.html">ditching</a> - GitHub after - <a href="https://github.blog/news-insights/company-news/an-update-on-github-availability">recent issues</a> - are looking to self-hosting Forgejo as an alternative. + <a href="https://mitchellh.com/writing/ghostty-leaving-github">People</a> <a href="https://vinyl-cache.org/organization/moving.html">ditching</a> GitHub after <a href="https://github.blog/news-insights/company-news/an-update-on-github-availability">recent issues</a> are looking to self-hosting Forgejo as an alternative. </p> <p>Despite some (poorly disclosed) <a href="https://dustri.org/b/carrot-disclosure-forgejo.html">security issues</a>, I think there's a more fundemental reason why you shouldn't use Forgejo.</p> <p>If you're just a single person trying to publish your independent coding projects, why do you need: CI, Issue Tracking, Pull Requests, Multi-user account management, etc?</p> @@ -3,12 +3,13 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <title>Collin Williams</title> + <script src="stars.js"></script> - <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="static/fonts.css"> <style> - @import url("static/fonts.css"); - *, *::before, *::after { box-sizing: border-box; } @@ -62,8 +63,9 @@ <h1 style="text-align: center;">Collin Williams</h1> <div class="cards"> <a class="card" href="https://git.collin.williamsfam.us.com">Projects</a> - <a class="card" href="./writing">Writing</a> + <a class="card" href="./writing.html">Writing</a> </div> </div> </body> </html> + @@ -1,13 +1,25 @@ <!DOCTYPE html> <html> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> + <title>Minimal Laptop Networking with NixOS</title> - <meta property="article:published_time" content="2026-07-29"> - <script src="article.js"></script> </head> <body> - <article> - <header></header> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>Minimal Laptop Networking with NixOS</h1> + <time datetime="2026-07-29">Published Jul 07, 2026</time> + </div> + </div> + <hr> + </header> <p> The default networking stack on NixOS uses NetworkManager. @@ -19,9 +31,17 @@ If you're configuring a server, you probably don't need iwd at all; systemd-networkd and systemd-resolved are usually sufficient. </p> - <div id="toc"></div> + <h2>Table of Contents</h2> + <ul> + <li><a href="#overview">The default stack</a></li> + <li><a href="#iwd">iwd</a></li> + <li><a href="#networkd">systemd-networkd</a></li> + <li><a href="#resolved">systemd-resolved</a></li> + <li><a href="#captiveportal">P.S. Captive portals</a></li> + <li><a href="#nm-applet">P.P.S. I miss nm-applet</a></li> + </ul> - <h2>The default stack</h2> + <h2 id="overview">The default stack</h2> <p>If not configured otherwise, NixOS uses the following stack:</p> <ul> <li>NetworkManager for wifi</li> @@ -54,7 +74,7 @@ <p>And let's get to configuring!</p> - <h2>iwd</h2> + <h2 id="iwd">iwd</h2> <p> First up is iwd. The important parts of the configuration are delegating network configuration to networkd, delegating name resolution (DNS) to resolved, and configuring MAC randomization for some extra privacy. @@ -82,7 +102,7 @@ <li><code><kw>iwctl</kw> <prop>station</prop> <prop>wlan0</prop> <prop>connect</prop> <str>"<SSID>"</str></code>: connects to the network, prompting for a PSK if necessary</li> </ul> - <h2>systemd-networkd</h2> + <h2 id="networkd">systemd-networkd</h2> <p> Next up is systemd-networkd. Networkd has lots of advanced options for things like static IP config, Wireguard tunnels, VLANs, domain-based DNS routing, and more. @@ -123,7 +143,7 @@ <li><code><kw>networkctl</kw> <prop>status</prop></code>: make sure there's no significant errors in the logs.</li> </ul> - <h2>systemd-resolved</h2> + <h2 id="resolved">systemd-resolved</h2> <p> Now all we have left is DNS configuration, then we're done. This is probably the part where you can nerd out the hardest, but I try to keep it pretty minimal. @@ -152,7 +172,7 @@ <p>You can verify your DNS configuration by using the <code><kw>resolvectl</kw> <prop>status</prop></code> command or by just opening a website.</p> - <h2>P.S. Captive portals</h2> + <h2 id="captiveportal">P.S. Captive portals</h2> <p> Captive portals are those wifi login pages that pop up when you connect to some public wifi networks. Some captive portals don't behave correctly when you're using an encrypted third-party DNS server instead of the network's advertised resolver. @@ -162,7 +182,7 @@ Pretty neat! </p> - <h2>P.P.S. I miss nm-applet</h2> + <h2 id="nm-applet">P.P.S. I miss nm-applet</h2> <p> Probably the biggest downside to this setup is the lack of desktop integration. GNOME and Plasma only support NetworkManager in their network configuration menu, so you're kinda out of luck. @@ -1,11 +1,16 @@ <!DOCTYPE html> <html> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> + + <title>Taking a break</title> - <script src="article.js"></script> </head> <body> - <article> + <article class="markdown-body"> <p> Prolonged access to LLMs is making me stupid. I can't code as fluently as I used to. diff --git a/reversible.html b/reversible.html index 893b73a..a9b6477 100644 --- a/reversible.html +++ b/reversible.html @@ -1,66 +1,90 @@ <!DOCTYPE html> <html> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> -<head> - <title>Reversible Programming is cool (Reversible Part 1)</title> - <meta property="article:published_time" content="2026-07-05"> - <script src="./article.js"></script> -</head> + <title>Reversible Programming is cool (Reversible Part 1)</title> + </head> -<body> - <article> - <header></header> + <body> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>Reversible Programming is cool (Reversible Part 1)</h1> + <time datetime="2026-07-29">Published Jul 05, 2026</time> + </div> + </div> + <hr> + </header> - <p> - Okay, a few days ago I learned about <a href="https://en.wikipedia.org/wiki/Reversible_programming_language">Reversible Programming</a>. - I decided to write a little Rust DSL to implement some of the stuff I learned. - In this article I walk through the code for my implementation and talk about some of the stuff you could do with - it. - </p> + <p> + Okay, a few days ago I learned about <a href="https://en.wikipedia.org/wiki/Reversible_programming_language">Reversible Programming</a>. + I decided to write a little Rust DSL to implement some of the stuff I learned. + In this article I walk through the code for my implementation and talk about some of the stuff you could do with it. + </p> - <div id="toc"></div> + <h2>Table of Contents</h2> + <ul> + <li><a href="#math">Math basis</a></li> + <li><a href="#idea">Idea of the implementation</a></li> + <li><a href="#foundations">Foundations</a></li> + <li><a href="#first">First Combinators</a></li> + <li><a href="#playground">Playing around</a></li> + <li><a href="#under">Under</a></li> + <li><a href="#next">Next Up</a></li> + </ul> - <h2 id="math">Math basis</h2> - <p> - So remember in math class when you learned to take the inverse of a function? - For example, say you have <code>y = 3*x + 6</code>. - You can take the inverse by solving for <code>x</code>. - In this case, the inverse of our function is <code>x = y/3 - 2</code>. - The idea behind reversible programming is that there's a specific way to write your code such that it can <strong>automatically compute the inverse</strong>. - </p> - <p>This has some wild implications:</p> - <ul> - <li>Writing a parser automatically gives you a pretty-printer implementation</li> - <li>Database migrations are only <em>half</em> as scary (writing your <code><fn>up</fn></code> script automatically gives you your <code><fn>down</fn></code> script)</li> - <li>Writing operations on an image (ex. Photoshop clone) gives you free undo and redo.</li> - </ul> - <p> - These are just a few of the crazy things you could <em>theoretically</em> do with reversible programming. - But how does it actually work? - </p> + <h2 id="math">Math basis</h2> + <p> + So remember in math class when you learned to take the inverse of a function? + For example, say you have <code>y = 3*x + 6</code>. + You can take the inverse by solving for <code>x</code>. + In this case, the inverse of our function is <code>x = y/3 - 2</code>. + The idea behind reversible programming is that there's a specific way to write your code such that it can <strong>automatically compute the inverse</strong>. + </p> + <p>This has some wild implications:</p> + <ul> + <li>Writing a parser automatically gives you a pretty-printer implementation</li> + <li>Database migrations are only <em>half</em> + as scary (writing your <code><fn>up</fn></code> + script automatically gives you your <code><fn>down</fn></code> + script)</li> + <li>Writing operations on an image (ex. Photoshop clone) gives you free undo and redo.</li> + </ul> + <p> + These are just a few of the crazy things you could <em>theoretically</em> + do with reversible programming. + But how does it actually work? + </p> - <h2 id="idea">Idea of the implementation</h2> - <p> - The idea behind this is that we can build small blocks of computation that we manually code the inverse logic for. - Then we use higher-level combinators to compose these small blocks together. - As long as the building-blocks are reversible and the combinators are implemented correctly, we'll have a fully reversible program. - </p> + <h2 id="idea">Idea of the implementation</h2> + <p> + The idea behind this is that we can build small blocks of computation that we manually code the inverse logic for. + Then we use higher-level combinators to compose these small blocks together. + As long as the building-blocks are reversible and the combinators are implemented correctly, we'll have a fully reversible program. + </p> - <h2 id="foundations">Foundations</h2> - <p>As any Rust project starts, we'll define some shared behavior that our initial blocks of computation must implement.</p> - <pre><code><kw>trait</kw> <ty>Rev</ty> { + <h2 id="foundations">Foundations</h2> + <p>As any Rust project starts, we'll define some shared behavior that our initial blocks of computation must implement.</p> + <pre><code><kw>trait</kw> <ty>Rev</ty> { <kw>type</kw> <ty>Input</ty>; <kw>type</kw> <ty>Output</ty>; <kw>fn</kw> <fn>forward</fn>(&<kw>self</kw>, <id>i</id>: <kw>Self</kw>::<ty>Input</ty>) -> <kw>Self</kw>::<ty>Output</ty>; <kw>fn</kw> <fn>reverse</fn>(&<kw>self</kw>, <id>o</id>: <kw>Self</kw>::<ty>Output</ty>) -> <kw>Self</kw>::<ty>Input</ty>; }</code></pre> - - <p> - As a nice starter, let's write a block that would compute addition. - We can model this with a struct and an impl of the <code><ty>Rev</ty></code> trait. - </p> - <pre><code><kw>struct</kw> <ty>Addition</ty><<ty>A</ty>> { + + <p> + As a nice starter, let's write a block that would compute addition. + We can model this with a struct and an impl of the <code><ty>Rev</ty></code> + trait. + </p> + <pre><code><kw>struct</kw> <ty>Addition</ty><<ty>A</ty>> { <prop>amount</prop>: <ty>A</ty> } @@ -82,38 +106,42 @@ } }</code></pre> - <p> - While the generics might look scary, this is just saying "the input and output can be any type that you can add and subtract with." - <span class="comment">Honestly these are light work compared to what's coming up.</span> - </p> - <p>Oh, and it will look nicer if we have an easy way to construct an <code>Addition</code> block:</p> - <pre><code><kw>fn</kw> <fn>plus</fn><<ty>A</ty>>(<id>amount</id>: <ty>A</ty>) -> <ty>Addition</ty><<ty>A</ty>> { + <p> + While the generics might look scary, this is just saying "the input and output can be any type that you can add and subtract with." + <span class="comment">Honestly these are light work compared to what's coming up.</span> + </p> + <p>Oh, and it will look nicer if we have an easy way to construct an <code>Addition</code> + block:</p> + <pre><code><kw>fn</kw> <fn>plus</fn><<ty>A</ty>>(<id>amount</id>: <ty>A</ty>) -> <ty>Addition</ty><<ty>A</ty>> { <ty>Addition</ty> { <prop>amount</prop> } }</code></pre> - <p>So let's see it in action!</p> - <pre><code class="language-rust"><kw>fn</kw> <fn>main</fn>() { + <p>So let's see it in action!</p> + <pre><code class="language-rust"><kw>fn</kw> <fn>main</fn>() { <kw>let</kw> <id>operation</id> = <fn>plus</fn>(<num>5</num>); <mac>println!</mac>(<str>"{}"</str>, <id>operation</id>.<fn>forward</fn>(<num>6</num>)); <com>// prints 11 (6 + 5 = 11)</com> <mac>println!</mac>(<str>"{}"</str>, <id>operation</id>.<fn>reverse</fn>(<num>11</num>)); <com>// prints 6 (11 - 5 = 6)</com> }</code></pre> - <p>Obviously nothing special so far. We're just using the inverse of addition, which is subtraction, just like our - trait impl said.</p> - <p>I've implemented a few more in the <a href="./reversible.rs#:~:text=%2F%2F%20more%20blocks">source code</a>:</p> - <ul> - <li><code><fn>neg</fn>()</code> negates a number</li> - <li><code><fn>times</fn>(x)</code> multiplies a number by x</li> - <li><code><fn>discrete</fn>(x: <ty>Vec</ty><(<ty>A</ty>, <ty>B</ty>)>)</code> creates a reversible block out of a set of discrete inputs and outputs.</li> - </ul> + <p>Obviously nothing special so far. We're just using the inverse of addition, which is subtraction, just like our + trait impl said.</p> + <p>I've implemented a few more in the <a href="./reversible.rs#:~:text=%2F%2F%20more%20blocks">source code</a>:</p> + <ul> + <li><code><fn>neg</fn>()</code> + negates a number</li> + <li><code><fn>times</fn>(x)</code> + multiplies a number by x</li> + <li><code><fn>discrete</fn>(x: <ty>Vec</ty><(<ty>A</ty>, <ty>B</ty>)>)</code> + creates a reversible block out of a set of discrete inputs and outputs.</li> + </ul> - <h2 id="first">First Combinators</h2> - <p> - The first combinator we'll implement is <code><ty>Pipe</ty></code>. - This takes in two <code>Rev</code>s and and makes one that applies them in sequential order. - </p> - <pre><code><kw>struct</kw> <ty>Pipe</ty><<ty>X</ty>, <ty>Y</ty>>(<ty>X</ty>, <ty>Y</ty>); + <h2 id="first">First Combinators</h2> + <p> + The first combinator we'll implement is <code><ty>Pipe</ty></code>. + This takes in two <code>Rev</code>s and and makes one that applies them in sequential order. + </p> + <pre><code><kw>struct</kw> <ty>Pipe</ty><<ty>X</ty>, <ty>Y</ty>>(<ty>X</ty>, <ty>Y</ty>); <kw>impl</kw><<ty>X</ty>, <ty>Y</ty>> <ty>Rev</ty> <kw>for</kw> <ty>Pipe</ty><<ty>X</ty>, <ty>Y</ty>> <kw>where</kw> <ty>X</ty>: <ty>Iso</ty>, @@ -131,11 +159,12 @@ } }</code></pre> - <p> - Additionally, we'll implement <code><ty>Inverse</ty></code> (not to be confused with <code>.<fn>reverse</fn>()</code>). - This flips the forward and reverse methods. - </p> - <pre><code><kw>struct</kw> <ty>Inverse</ty><<ty>X</ty>>(<ty>X</ty>); + <p> + Additionally, we'll implement <code><ty>Inverse</ty></code> + (not to be confused with <code>.<fn>reverse</fn>()</code>). + This flips the forward and reverse methods. + </p> + <pre><code><kw>struct</kw> <ty>Inverse</ty><<ty>X</ty>>(<ty>X</ty>); <kw>impl</kw><<ty>X</ty>: <ty>Iso</ty>> <ty>Rev</ty> <kw>for</kw> <ty>Inverse</ty><<ty>X</ty>> { <kw>type</kw> <ty>Input</ty> = <ty>X</ty>::<ty>Output</ty>; <kw>type</kw> <ty>Output</ty> = <ty>X</ty>::<ty>Input</ty>; @@ -149,11 +178,11 @@ } }</code></pre> - <p> - And finally, it will look nicer if we can use a chaining syntax to apply these combinators. - We'll do that using an extension trait: - </p> - <pre><code><kw>trait</kw> <ty>RevExt</ty>: <ty>Rev</ty> + <ty>Sized</ty> { + <p> + And finally, it will look nicer if we can use a chaining syntax to apply these combinators. + We'll do that using an extension trait: + </p> + <pre><code><kw>trait</kw> <ty>RevExt</ty>: <ty>Rev</ty> + <ty>Sized</ty> { <kw>fn</kw> <fn>then</fn><<ty>N</ty>: <ty>Rev</ty><<ty>Input</ty> = <kw>Self</kw>::<ty>Output</ty>>>(<kw>self</kw>, <id>other</id>: <ty>N</ty>) -> <ty>Pipe</ty><<kw>Self</kw>, <ty>N</ty>> { <ty>Pipe</ty>(<kw>self</kw>, <id>other</id>) } @@ -163,14 +192,14 @@ } } <kw>impl</kw><<ty>T</ty>: <ty>Rev</ty>> <ty>RevExt</ty> <kw>for</kw> <ty>T</ty> {}</code></pre> - - <h2 id="playground">Playing around</h2> - <p> - So, what can we do with these combinators? - Quite a lot, actually! - Our math example from the beginning is trivial now: - </p> - <pre><code><kw>fn</kw> <fn>main</fn>() { + + <h2 id="playground">Playing around</h2> + <p> + So, what can we do with these combinators? + Quite a lot, actually! + Our math example from the beginning is trivial now: + </p> + <pre><code><kw>fn</kw> <fn>main</fn>() { <kw>let</kw> <id>y</id> = <fn>times</fn>(<num>3</num>).<fn>then</fn>(<fn>plus</fn>(<num>6</num>)); <kw>let</kw> <id>x</id> = <id>y</id>.<fn>inverse</fn>(); <com>// We don't have to know how this gets computed!</com> @@ -178,8 +207,8 @@ <mac>println!</mac>(<str>"{}"</str>, <id>x</id>.<fn>forward</fn>(<num>30</num>)); <com>// prints 8</com> }</code></pre> - <p>Here's a little celsius to farenheit calculator that only encodes the transformation one way:</p> - <pre><code><kw>fn</kw> <fn>main</fn>() { + <p>Here's a little celsius to farenheit calculator that only encodes the transformation one way:</p> + <pre><code><kw>fn</kw> <fn>main</fn>() { <com>// F = (C * 1.8) + 32</com> <kw>let</kw> <id>celsius_to_fahrenheit</id> = <fn>times</fn>(<num>1.8</num>).<fn>then</fn>(<fn>add</fn>(<num>32.0</num>)); @@ -192,13 +221,14 @@ <mac>println!</mac>(<str>"{}"</str>, <id>fahrenheit_to_celsius</id>.<fn>forward</fn>(<num>32.0</num>)); <com>// prints 0.0</com> }</code></pre> - <h2 id="under">Under</h2> - <p> - <code><fn>under</fn></code> is a higher order combinator, which means it's implemented in terms of other combinators. - Under basically says "do a thing, do another thing, then undo the first thing. - With this foundation built up, we can implement it quite easily: - </p> - <pre><code><kw>fn</kw> <fn>under</fn><<ty>Op</ty>, <ty>Val</ty>>(<id>op</id>: <ty>Op</ty>, <id>val</id>: <ty>Val</ty>) -> <kw>impl</kw> <ty>Rev</ty>< + <h2 id="under">Under</h2> + <p> + <code><fn>under</fn></code> + is a higher order combinator, which means it's implemented in terms of other combinators. + Under basically says "do a thing, do another thing, then undo the first thing. + With this foundation built up, we can implement it quite easily: + </p> + <pre><code><kw>fn</kw> <fn>under</fn><<ty>Op</ty>, <ty>Val</ty>>(<id>op</id>: <ty>Op</ty>, <id>val</id>: <ty>Val</ty>) -> <kw>impl</kw> <ty>Rev</ty>< <ty>Input</ty> = <ty>Op</ty>::<ty>Input</ty>, <ty>Output</ty> = <ty>Op</ty>::<ty>Input</ty> > @@ -209,11 +239,11 @@ <id>op</id>.<fn>clone</fn>().<fn>then</fn>(<id>val</id>).<fn>then</fn>(<id>op</id>.<fn>inverse</fn>()) }</code></pre> - <p> - We can use this to encode "setup operations." - For example, this code takes in a number string, and returns a number string, but does an operation on integers in-between. - </p> - <pre><code><kw>fn</kw> <fn>main</fn>() { + <p> + We can use this to encode "setup operations." + For example, this code takes in a number string, and returns a number string, but does an operation on integers in-between. + </p> + <pre><code><kw>fn</kw> <fn>main</fn>() { <kw>let</kw> <id>num_as_str</id> = <fn>discrete</fn>(<mac>vec!</mac>[ (<str>"one"</str>, <num>1</num>), (<str>"two"</str>, <num>2</num>), @@ -228,15 +258,14 @@ <mac>println!</mac>(<str>"{}"</str>, <id>operation</id>.<fn>forward</fn>(<str>"three"</str>)); <com>// prints "six"</com> <mac>println!</mac>(<str>"{}"</str>, <id>operation</id>.<fn>reverse</fn>(<str>"five"</str>)); <com>// prints "two"</com> }</code></pre> - - <h2 id="next">Next Up</h2> - <p> - Well, that's all I've got for today. - Next time, we'll look at data-structures, like sum and product types, and maybe some other stuff. - The source code for this project so far is available <a href="./reversible.rs">here</a>. - Stay tuned! - </p> - </article> -</body> + <h2 id="next">Next Up</h2> + <p> + Well, that's all I've got for today. + Next time, we'll look at data-structures, like sum and product types, and maybe some other stuff. + The source code for this project so far is available <a href="./reversible.rs">here</a>. + Stay tuned! + </p> + </article> + </body> </html> diff --git a/reversible.rs b/reversible.rs deleted file mode 100644 index 19e6f98..0000000 --- a/reversible.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::marker::PhantomData; - -pub trait Rev { - type Input; - type Output; - - fn forward(&self, i: Self::Input) -> Self::Output; - fn reverse(&self, o: Self::Output) -> Self::Input; -} - -#[derive(Clone)] -pub struct Addition<A> { - amount: A -} -impl<A> Rev for Addition<A> -where - A: std::ops::Add<Output = A> + std::ops::Sub<Output = A> + Copy -{ - type Input = A; - type Output = A; - - fn forward(&self, a: A) -> A { - a + self.amount - } - - fn reverse(&self, b: A) -> A { - b - self.amount - } -} -pub fn plus<A>(amount: A) -> Addition<A> { - Addition { amount } -} - -// more blocks - -#[derive(Clone)] -pub struct Negate<A>(PhantomData<A>); -impl<A: std::ops::Neg<Output = A>> Rev for Negate<A> { - type Input = A; - type Output = A; - - fn forward(&self, a: Self::Input) -> Self::Output { - -a - } - - fn reverse(&self, b: Self::Output) -> Self::Input { - -b - } -} -pub fn neg<A>() -> Negate<A> { - Negate(PhantomData) -} - -#[derive(Clone)] -pub struct Multiplication<A> { - amount: A -} -impl<A> Rev for Multiplication<A> -where - A: std::ops::Mul<Output = A> + std::ops::Div<Output = A> + Copy, -{ - type Input = A; - type Output = A; - - fn forward(&self, a: A) -> A { - a * self.amount - } - - fn reverse(&self, b: A) -> A { - b / self.amount - } -} -pub fn times<A>(amount: A) -> Multiplication<A> { - Multiplication { amount } -} - -#[derive(Clone)] -pub struct DiscreteMap<A, B>(Vec<(A, B)>); -impl<A: PartialEq + Clone, B: PartialEq + Clone> Rev for DiscreteMap<A, B> { - type Input = A; - type Output = B; - - fn forward(&self, a: A) -> B { - for i in self.0.iter() { - if i.0 == a { - return i.1.clone() - } - } - panic!() - } - - fn reverse(&self, b: B) -> A { - for i in self.0.iter() { - if i.1 == b { - return i.0.clone() - } - } - panic!() - } -} -pub fn discrete<A, B>(vec: Vec<(A, B)>) -> DiscreteMap<A, B> { - DiscreteMap(vec) -} - -// combinators - -#[derive(Clone)] -pub struct Pipe<X, Y>(X, Y); -impl<X, Y> Rev for Pipe<X, Y> -where - X: Rev, - Y: Rev<Input = X::Output> -{ - type Input = X::Input; - type Output = Y::Output; - - fn forward(&self, a: Self::Input) -> Self::Output { - self.1.forward(self.0.forward(a)) - } - - fn reverse(&self, c: Self::Output) -> Self::Input { - self.0.reverse(self.1.reverse(c)) - } -} - -#[derive(Clone)] -pub struct Inverse<X>(X); -impl<X: Rev> Rev for Inverse<X> { - type Input = X::Output; - type Output = X::Input; - - fn forward(&self, a: Self::Input) -> Self::Output { - self.0.reverse(a) - } - - fn reverse(&self, b: Self::Output) -> Self::Input { - self.0.forward(b) - } -} - -pub trait RevExt: Rev + Sized { - fn then<N: Rev<Input = Self::Output>>(self, other: N) -> Pipe<Self, N> { - Pipe(self, other) - } - - fn inverse(self) -> Inverse<Self> { - Inverse(self) - } -} -impl<T: Rev> RevExt for T {} - -pub fn under<Op, Val>(op: Op, val: Val) -> impl Rev<Input = Op::Input, Output = Op::Input> -where - Op: Rev + Clone, - Val: Rev<Input = Op::Output, Output = Op::Output> -{ - op.clone().then(val).then(op.inverse()) -}
\ No newline at end of file @@ -1,96 +1,122 @@ <!DOCTYPE html> <html> -<head> - <title>Summer of PLT</title> - <meta property="article:published_time" content="2026-05-10"> - <script src="article.js"></script> -</head> -<body> - <article> - <header></header> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="icon" href="static/Logo.svg" type="image/svg+xml"> + <link rel="stylesheet" href="article.css"> - <p>Looking for a summer coding project? Try making a programming language!</p> - <p>I think that every programmer, new or experienced, could benefit from understanding a little bit about how the languages they use daily actually work.</p> - <p>As such, this is my collection of resources that I used to teach myself a little bit of Programming Language Theory last summer, which concluded in my creation of <a href="https://github.com/bluedragon1221/ci">CI lisp</a>.</p> - <p>I'm planning on doing something similar this summer, so I figured I'd invite other people to try a 'Summer of PLT' too.</p> - <p class="comment">Disclaimer: I'm just a guy with no formal education in PLT. Like <a href="https://danilafe.com/blog/i_love_programming_languages">Daniel Fedorin said</a>, I just <em>love the field of programming languages</em>.</p> + <title>Summer of PLT</title> + </head> + <body> + <article class="markdown-body"> + <header> + <div class="header-top"> + <a href="writing.html" class="header-logo-link"></a> + <div class="header-info"> + <h1>Summer of PLT</h1> + <time datetime="2026-05-10">Published May 05, 2026</time> + </div> + </div> + <hr> + </header> - <div id="toc"></div> + <p>Looking for a summer coding project? Try making a programming language!</p> + <p>I think that every programmer, new or experienced, could benefit from understanding a little bit about how the languages they use daily actually work.</p> + <p>As such, this is my collection of resources that I used to teach myself a little bit of Programming Language Theory last summer, which concluded in my creation of <a href="https://github.com/bluedragon1221/ci">CI lisp</a>.</p> + <p>I'm planning on doing something similar this summer, so I figured I'd invite other people to try a 'Summer of PLT' too.</p> + <p class="comment">Disclaimer: I'm just a guy with no formal education in PLT. Like <a href="https://danilafe.com/blog/i_love_programming_languages">Daniel Fedorin said</a>, I just <em>love the field of programming languages</em>.</p> - <h2 id="read">Read a Book</h2> - <p> - If you're new to the field of programming languages, my first piece of advice is to read Crafting Interpreters! - <span style="color: #666">(or at least the first section, "A Treewalking Interpreter")</span> - In my opinion, there is no better way to break into the field of PLT than by reading Bob Nystrom's book on the subject: <a href="https://craftinginterpreters.com">Crafting Interpreters</a>. - There's also a free HTML version on his site if you don't feel like paying money. - </p> + <h2>Table of Contents</h2> + <ul> + <li><a href="#read">Read a Book</a></li> + <li><a href="#lisp">Make a Lisp</a></li> + <li><a href="#next">What next?</a></li> + <li><a href="#lambda">Lambda Calculus</a></li> + <li><a href="#monads">Monadic IO</a></li> + <li><a href="#sugar">Syntactic Sugar</a></li> + <li><a href="#chompchomp">Bytecode VM</a></li> + <li><a href="#cas">Make a CAS (?)</a></li> + <li><a href="#no">Try something else</a></li> + <li><a href="#contact">Contact</a></li> + </ul> - <p>After that, I would suggest looking into Lisp.</p> + <h2 id="read">Read a Book</h2> + <p> + If you're new to the field of programming languages, my first piece of advice is to read Crafting Interpreters! + <span style="color: #666">(or at least the first section, "A Treewalking Interpreter")</span> + In my opinion, there is no better way to break into the field of PLT than by reading Bob Nystrom's book on the subject: <a href="https://craftinginterpreters.com">Crafting Interpreters</a>. + There's also a free HTML version on his site if you don't feel like paying money. + </p> - <h2 id="lisp">Make a Lisp</h2> - <p>Lisp is one of the foundational language of PLT. My advice to anyone trying to start in this field would be to make a Lisp. Its minimal syntax makes parsing and interpreting much easier.</p> - <p>For a nice overview (and a bit of history), check out the Wikipedia page on Lisp: <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp (programming language)</a>.</p> - - <p>Before making your lisp, it would probably be good if you knew Lisp. Here's a good resource for that: <a href="https://lisp-lang.org/learn/getting-started">lisp-lang.org</a>.</p> - <p class="comment">Alternatively, you could consider going down the Emacs rabbit hole to teach yourself lisp. In the Emacs environment, you'll quickly gain proficiency with the parentheses, but you'll probably lose at least a few weeks in the process.</p> + <p>After that, I would suggest looking into Lisp.</p> - <p>Theory-wise, I would recommend reading the original John McCarthy Lisp paper (be prepared for dense academic writing though): <a href="https://www.cs.tufts.edu/~nr/cs257/archive/john-mccarthy/recursive.pdf">Recursive Functions of Symbolic Expressions and Their Computation by Machine</a>.</p> - <p>Additionally, many suggest the book <a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html">Structure and Interpretation of Computer Programs</a>, which also includes some </p> + <h2 id="lisp">Make a Lisp</h2> + <p>Lisp is one of the foundational language of PLT. My advice to anyone trying to start in this field would be to make a Lisp. Its minimal syntax makes parsing and interpreting much easier.</p> + <p>For a nice overview (and a bit of history), check out the Wikipedia page on Lisp: <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp (programming language)</a>.</p> - <p>The C2 lisp rant is also a fun read (but by no means required): <a href="https://wiki.c2.com/?LostInaSeaofParentheses">Lost Ina Seaof Parentheses</a>.</p> + <p>Before making your lisp, it would probably be good if you knew Lisp. Here's a good resource for that: <a href="https://lisp-lang.org/learn/getting-started">lisp-lang.org</a>.</p> + <p class="comment">Alternatively, you could consider going down the Emacs rabbit hole to teach yourself lisp. In the Emacs environment, you'll quickly gain proficiency with the parentheses, but you'll probably lose at least a few weeks in the process.</p> - <p>If you want more of a guided, handhold-y tutorial for making a lisp clone, I can only recommend <a href="https://github.com/kanaka/mal">mal - Make a Lisp</a>. I personally have not gone through it, but I've heard good things.</p> - <p class="comment">Alternatively, ask an LLM to help you learn how programming languages work. Just make sure that you're actually learning, not just copying whatever it says to do. Try something like "Don't give me any code, just explain to me..."</p> + <p>Theory-wise, I would recommend reading the original John McCarthy Lisp paper (be prepared for dense academic writing though): <a href="https://www.cs.tufts.edu/~nr/cs257/archive/john-mccarthy/recursive.pdf">Recursive Functions of Symbolic Expressions and Their Computation by Machine</a>.</p> + <p>Additionally, many suggest the book <a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html">Structure and Interpretation of Computer Programs</a>, which also includes some</p> - <h2 id="next">What next?</h2> - <p>After you make your lisp, you can use it as a playground for more ideas in the field of PLT.</p> + <p>The C2 lisp rant is also a fun read (but by no means required): <a href="https://wiki.c2.com/?LostInaSeaofParentheses">Lost Ina Seaof Parentheses</a>.</p> - <h3 id="lambda">Lambda Calculus</h3> - <p> - If you find yourself seeking more of the theory-side of PLT, consider learning Lambda Calculus and Church Encoding and implementing them in your lisp! - </p><p> - There's tons of resources on Lambda Calculus, but you can't go wrong with a skim of Alonzo Church's original book on the subject, <a href="https://compcalc.github.io/public/church/church_calculi_1941.pdf">The Calculi of Lambda-Conversion</a> - </p><p>Or you don't feel up to reading 1940s academic rhetoric, the Wikipedia page on Lambda Calculus is nice: <a href="https://en.wikipedia.org/wiki/Lambda_calculus">Lambda calculus</a> - </p> + <p>If you want more of a guided, handhold-y tutorial for making a lisp clone, I can only recommend <a href="https://github.com/kanaka/mal">mal - Make a Lisp</a>. I personally have not gone through it, but I've heard good things.</p> + <p class="comment">Alternatively, ask an LLM to help you learn how programming languages work. Just make sure that you're actually learning, not just copying whatever it says to do. Try something like "Don't give me any code, just explain to me..."</p> - <h3 id="monads">Monadic IO</h3> - <p>Also on the theory-heavy side, you could learn about Haskell's system for safe IO operations which use monads.</p> - <p>This paper on the subject (discovered on the Haskell wiki) is a pretty dense, but has all of the foundational ideas for Monadic IO in Haskell: <a href="https://web.archive.org/web/20140303101716/http://www-fp.dcs.st-and.ac.uk/~kh/papers/io-tutorial/io-tutorial.html">Monadic I/O in Haskell 1.3</a>.</p> - <p>The foundational ideas of Monads are well explained in <a href="https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf">Monads for functional programming</a> by Phillip Wadler. I found this paper easier to understand than the prior one, and it gives you a great intuition for <em>why</em> Monads are cool. Definitely read this one!</p> + <h2 id="next">What next?</h2> + <p>After you make your lisp, you can use it as a playground for more ideas in the field of PLT.</p> - <h3 id="sugar">Syntactic Sugar</h3> - <p> - If you're looking more into the practical side of PLT, you could look into adding syntactic sugar to your lisp. - One source to look into might be David Wheeler's <a href="https://dwheeler.com/readable/sweet-expressions.html">Sweet Expressions</a>. - </p> + <h3 id="lambda">Lambda Calculus</h3> + <p> + If you find yourself seeking more of the theory-side of PLT, consider learning Lambda Calculus and Church Encoding and implementing them in your lisp! + </p><p> + There's tons of resources on Lambda Calculus, but you can't go wrong with a skim of Alonzo Church's original book on the subject, <a href="https://compcalc.github.io/public/church/church_calculi_1941.pdf">The Calculi of Lambda-Conversion</a> + </p><p>Or you don't feel up to reading 1940s academic rhetoric, the Wikipedia page on Lambda Calculus is nice: <a href="https://en.wikipedia.org/wiki/Lambda_calculus">Lambda calculus</a></p> - <h3 id="chompchomp">Bytecode VM</h3> - <p> - If you're interested in becoming more like the "real programming languages", you could look into writing a bytecode VM for your language. - It's a big undertaking, but it's outlined very well in the second section of Crafting Interpreters, <a href="https://craftinginterpreters.com/a-bytecode-virtual-machine.html">A Bytecode Virtual Machine</a>. - </p> + <h3 id="monads">Monadic IO</h3> + <p>Also on the theory-heavy side, you could learn about Haskell's system for safe IO operations which use monads.</p> + <p>This paper on the subject (discovered on the Haskell wiki) is a pretty dense, but has all of the foundational ideas for Monadic IO in Haskell: <a href="https://web.archive.org/web/20140303101716/http://www-fp.dcs.st-and.ac.uk/~kh/papers/io-tutorial/io-tutorial.html">Monadic I/O in Haskell 1.3</a>.</p> + <p>The foundational ideas of Monads are well explained in <a href="https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf">Monads for functional programming</a> + by Phillip Wadler. I found this paper easier to understand than the prior one, and it gives you a great intuition for <em>why</em> + Monads are cool. Definitely read this one!</p> - <h3 id="cas">Make a CAS (?)</h3> - <p> - If you're a math nerd (like me), you could look into writing a CAS hosted in your lisp. - For me, this meant writing a deep pattern matching system and substitution engine. - Definitely a fun project! - </p> + <h3 id="sugar">Syntactic Sugar</h3> + <p> + If you're looking more into the practical side of PLT, you could look into adding syntactic sugar to your lisp. + One source to look into might be David Wheeler's <a href="https://dwheeler.com/readable/sweet-expressions.html">Sweet Expressions</a>. + </p> - <h3 id="no">Try something else</h3> - <p>Maybe you're sick of lisp? That's fine! There's lots of other PLT stuff that might suit your fancy:</p> - <ul> - <li><p>Make a Forth -- Sorry, I don't have very much information on this one, but stack-based languages are pretty neat!</p></li> - <li><p>Make a Python -- You can extend the ideas of lexing and parsing to a more complicated syntax, leading to more of a Python-like experience</p></li> - </ul> + <h3 id="chompchomp">Bytecode VM</h3> + <p> + If you're interested in becoming more like the "real programming languages", you could look into writing a bytecode VM for your language. + It's a big undertaking, but it's outlined very well in the second section of Crafting Interpreters, <a href="https://craftinginterpreters.com/a-bytecode-virtual-machine.html">A Bytecode Virtual Machine</a>. + </p> - <h2 id="contact">Contact</h2> - <p>Did I interest you in a 'Summer of PLT'? Let me know! I'd love to see some toy lisps this summer!</p> + <h3 id="cas">Make a CAS (?)</h3> + <p> + If you're a math nerd (like me), you could look into writing a CAS hosted in your lisp. + For me, this meant writing a deep pattern matching system and substitution engine. + Definitely a fun project! + </p> - <footer> - <hr> - <p class="comment">Discuss on <a href="https://news.ycombinator.com/item?id=48087351">Hacker news</a> or <a href="https://bsky.app/profile/cwilliams1221.bsky.social/post/3mljk2zarjc2h">Bluesky</a></p> - </footer> - </article> -</body> -</html>
\ No newline at end of file + <h3 id="no">Try something else</h3> + <p>Maybe you're sick of lisp? That's fine! There's lots of other PLT stuff that might suit your fancy:</p> + <ul> + <li><p>Make a Forth -- Sorry, I don't have very much information on this one, but stack-based languages are pretty neat!</p></li> + <li><p>Make a Python -- You can extend the ideas of lexing and parsing to a more complicated syntax, leading to more of a Python-like experience</p></li> + </ul> + + <h2 id="contact">Contact</h2> + <p>Did I interest you in a 'Summer of PLT'? Let me know! I'd love to see some toy lisps this summer!</p> + + <footer> + <hr> + <p class="comment">Discuss on <a href="https://news.ycombinator.com/item?id=48087351">Hacker news</a> + or <a href="https://bsky.app/profile/cwilliams1221.bsky.social/post/3mljk2zarjc2h">Bluesky</a></p> + </footer> + </article> + </body> +</html> diff --git a/static b/static -Subproject ec713388f29ea08f5931934f0c1d6d0e9ec214f +Subproject 03f93890672d169de4f77f0149b3a39c30be2ec diff --git a/writing.html b/writing.html index cec4bc1..1ad3695 100644 --- a/writing.html +++ b/writing.html @@ -19,13 +19,13 @@ <article class="markdown-body"> <h1>My Writing</h1> <ul> - <li><a href="./devshell">A solution to slow nix-shells</a></li> - <li><a href="./net">Minimal Laptop Networking with NixOS</a></li> - <li><a href="./reversible">Reversible Programming is cool (Reversible Part 1)</a></li> - <li><a href="./fib">I accidentally derived a formula for the Fibonacci Sequence</a></li> - <li><a href="./no_ai">Taking a Break</a></li> - <li><a href="./soplt">Summer of PLT</a></li> - <li><a href="./forgejno">You Don't Need Forgejo</a></li> + <li><a href="./devshell.html">A solution to slow nix-shells</a></li> + <li><a href="./net.html">Minimal Laptop Networking with NixOS</a></li> + <li><a href="./reversible.html">Reversible Programming is cool (Reversible Part 1)</a></li> + <li><a href="./fib.html">I accidentally derived a formula for the Fibonacci Sequence</a></li> + <li><a href="./no_ai.html">Taking a Break</a></li> + <li><a href="./soplt.html">Summer of PLT</a></li> + <li><a href="./forgejno.html">You Don't Need Forgejo</a></li> </ul> </article> </body> |
