<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Thomas Seeley</title><id>https://www.tseeley.com/atom.xml</id><updated>2026-01-15T00:00:00+00:00</updated><generator>tseeley.com</generator><link href="https://www.tseeley.com/atom.xml" rel="self" type="application/atom+xml"/><link href="https://www.tseeley.com" rel="alternate" type="text/html"/><subtitle>Posts about software and making things.</subtitle><entry><title>Nix Resources</title><id>https://www.tseeley.com/posts/nix-resources/</id><updated>2026-01-15T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/nix-resources/" rel="alternate" type="text/html"/><published>2026-01-15T00:00:00+00:00</published><content type="html">
&lt;section id=&quot;Start-here&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Start-here&quot;&gt;Start here&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://nix.dev&quot;&gt;nix.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://nixos-and-flakes.thiscute.world/&quot;&gt;NixOS &amp;amp; Flakes
Book&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Reference&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Reference&quot;&gt;Reference&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://search.nixos.org&quot;&gt;search.nixos.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://noogle.dev&quot;&gt;noogle.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://mynixos.com&quot;&gt;MyNixOS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://home-manager-options.extranix.com/&quot;&gt;home-manager
options&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Language-module-system&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Language-module-system&quot;&gt;Language + module system&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://nixos.org/guides/nix-pills/&quot;&gt;Nix Pills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://nix.dev/manual/nix/stable/&quot;&gt;Nix Reference Manual&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://nix.dev/tutorials/module-system/&quot;&gt;Module system
tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Community&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Community&quot;&gt;Community&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://discourse.nixos.org&quot;&gt;NixOS Discourse&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://wiki.nixos.org&quot;&gt;NixOS Wiki&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://reddit.com/r/NixOS&quot;&gt;r/NixOS&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Code&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Code&quot;&gt;Code&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/NixOS/nixpkgs&quot;&gt;nixpkgs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/Misterio77/nix-config&quot;&gt;Misterio77/nix-config&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/Mic92/dotfiles&quot;&gt;Mic92/dotfiles&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;nix&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/posts/onboarding/&quot;&gt;How To Onboard&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>How To Onboard</title><id>https://www.tseeley.com/posts/onboarding/</id><updated>2025-08-05T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/onboarding/" rel="alternate" type="text/html"/><published>2025-08-05T00:00:00+00:00</published><content type="html">
&lt;ol&gt;
&lt;li&gt;
Optimize for &lt;a href=&quot;https://en.wikipedia.org/wiki/Time_to_value&quot;&gt;time-to-value&lt;/a&gt;, not signup completion
&lt;/li&gt;
&lt;li&gt;
Never show a completely empty workspace
&lt;/li&gt;
&lt;li&gt;
Single, opinionated first action
&lt;/li&gt;
&lt;li&gt;
Personalize only when the answer changes the experience
&lt;/li&gt;
&lt;li&gt;
Teach by doing, not by tooltips
&lt;/li&gt;
&lt;li&gt;
Earn complexity over time
&lt;/li&gt;
&lt;li&gt;
Define and obsess over one activation milestone
&lt;/li&gt;
&lt;li&gt;
White-glove first to learn, then productize
&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;ux&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/nix-resources/&quot;&gt;Nix Resources&lt;/a&gt; · &lt;a href=&quot;https://www.tseeley.com/posts/inferring-state-types/&quot;&gt;Inferring types from usage&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>Inferring types from usage</title><id>https://www.tseeley.com/posts/inferring-state-types/</id><updated>2025-04-01T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/inferring-state-types/" rel="alternate" type="text/html"/><published>2025-04-01T00:00:00+00:00</published><content type="html">
&lt;p&gt;Membrane programs have a &lt;a href=&quot;https://docs.membrane.io/guides/state&quot;&gt;persistent &lt;code&gt;state&lt;/code&gt; object&lt;/a&gt; that survives across deploys. You import it and use it like a regular JavaScript object:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;import { &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;} from &amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;membrane&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;??= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;export &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;function increment&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;() {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;return ++&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The entire JS heap is durable, but &lt;code&gt;state&lt;/code&gt; is the conventional way to pass data between code versions. The question is: what type is &lt;code&gt;state&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;We could make developers define a &lt;code&gt;State&lt;/code&gt; type for every program. But that’s boilerplate, and for quick scripts and prototypes it slows you down. We already had a TypeScript language service plugin for Membrane’s graph types, so I added state type inference to it.&lt;/p&gt;
&lt;section id=&quot;idea&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#idea&quot;&gt;The Idea&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you write &lt;code&gt;state.count ??= 0&lt;/code&gt;, we know &lt;code&gt;state.count&lt;/code&gt; is a &lt;code&gt;number&lt;/code&gt;. If you write &lt;code&gt;state.apiKey = args.apiKey&lt;/code&gt; and &lt;code&gt;args.apiKey&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;, we know &lt;code&gt;state.apiKey&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;. Walk the abstract syntax tree&lt;a id=&quot;fnref1&quot; href=&quot;#fn1&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;, find every assignment to a &lt;code&gt;state&lt;/code&gt; property, extract the type from the right-hand side, and generate a type declaration.&lt;/p&gt;
&lt;p&gt;The generated type looks like this:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;type State &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  count&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;number;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  apiKey&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;string;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;key: string&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;]: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;any;
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gets injected into the generated &lt;code&gt;membrane.d.ts&lt;/code&gt; file. You get autocomplete and type checking on &lt;code&gt;state&lt;/code&gt; without writing any type annotations. If you do want to define the type explicitly, export a &lt;code&gt;State&lt;/code&gt; type or interface from &lt;code&gt;index.ts&lt;/code&gt; and the inference backs off.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;tricky&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#tricky&quot;&gt;The Tricky Parts&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The idea is simple, but as always implementation had more edge cases than expected.&lt;/p&gt;
&lt;section id=&quot;Assignment-Patterns&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Assignment-Patterns&quot;&gt;Assignment Patterns&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Turns out people assign to &lt;code&gt;state&lt;/code&gt; in a lot of different ways:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// direct assignment
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;??= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// nullish coalescing assignment
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;||= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// logical OR assignment
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;+= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// compound assignment
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;value &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;as &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// type assertion
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The AST visitor needs to catch all of these. Direct assignments and compound assignments (&lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, etc.) fall in a range of operator token kinds between &lt;code&gt;FirstAssignment&lt;/code&gt; and &lt;code&gt;LastAssignment&lt;/code&gt;, which makes them easy to handle together. Nullish coalescing assignment (&lt;code&gt;??=&lt;/code&gt;) is its own token kind and needs a separate check:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isBinaryExpression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) &amp;amp;&amp;amp;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;operatorToken&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kind &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;FirstAssignment &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;amp;&amp;amp;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;operatorToken&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kind &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;lt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;LastAssignment
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  processStateAssignment&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;right&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;} else if (
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isBinaryExpression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) &amp;amp;&amp;amp;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;operatorToken&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kind &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;=== &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;QuestionQuestionEqualsToken
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  processStateAssignment&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;right&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id=&quot;Nullish-Coalescing-and-any&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Nullish-Coalescing-and-any&quot;&gt;Nullish Coalescing and &lt;code&gt;any&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;state.count ??= 0&lt;/code&gt; is interesting because the right-hand side is a binary expression with &lt;code&gt;??&lt;/code&gt;. The left side is &lt;code&gt;state.count&lt;/code&gt;, which might be &lt;code&gt;any&lt;/code&gt; if we haven’t inferred a type for it yet. The right side is &lt;code&gt;0&lt;/code&gt;, which is &lt;code&gt;number&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we naively get the type of the whole expression, TypeScript might give us &lt;code&gt;any&lt;/code&gt; because one side is &lt;code&gt;any&lt;/code&gt;. So we check both sides individually:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;operatorToken&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kind &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;=== &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;QuestionQuestionToken &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;||
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;operatorToken&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kind &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;=== &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;BarBarToken
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;leftType &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;checker&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getTypeAtLocation&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;rightType &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;checker&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getTypeAtLocation&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;right&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isLeftAny &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;leftType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getFlags&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;() &amp;amp; &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;TypeFlags&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;Any&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) !== &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isRightAny &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;rightType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getFlags&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;() &amp;amp; &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;TypeFlags&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;Any&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) !== &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isLeftAny &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;amp;&amp;amp; !&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isRightAny&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) return &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;rightType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (!&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isLeftAny &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;amp;&amp;amp; &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isRightAny&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) return &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;leftType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;return &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;checker&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getTypeAtLocation&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If one side is &lt;code&gt;any&lt;/code&gt; and the other is concrete, use the concrete type. If both are concrete, use the union. This is what makes &lt;code&gt;state.count ??= 0&lt;/code&gt; correctly infer &lt;code&gt;number&lt;/code&gt; instead of &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;Explicit-vs-Inferred-Priority&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Explicit-vs-Inferred-Priority&quot;&gt;Explicit vs Inferred Priority&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Consider this:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;??= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// inferred: number
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getCount&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(); &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// explicit: number (from getCount&amp;#39;s return type)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both assignments tell us &lt;code&gt;state.count&lt;/code&gt; is a &lt;code&gt;number&lt;/code&gt;, but they have different confidence levels. A direct assignment (&lt;code&gt;=&lt;/code&gt;) is explicit. A nullish coalescing assignment (&lt;code&gt;??=&lt;/code&gt;) or logical OR assignment (&lt;code&gt;||=&lt;/code&gt;) is inferred, because they’re fallback patterns that might not represent the primary type.&lt;/p&gt;
&lt;p&gt;When we see a new assignment to a property we’ve already typed, explicit assignments override inferred ones. More specific types override &lt;code&gt;any&lt;/code&gt;. This means the order of assignments in your code doesn’t matter as much as the kind of assignment.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;Type-Assertions&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Type-Assertions&quot;&gt;Type Assertions&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you write &lt;code&gt;state.data = response as ApiResponse&lt;/code&gt;, the type should be &lt;code&gt;ApiResponse&lt;/code&gt;, not whatever TypeScript infers from &lt;code&gt;response&lt;/code&gt;. The plugin checks for &lt;code&gt;as&lt;/code&gt; expressions and type assertion expressions, unwrapping parenthesized expressions along the way:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;function &lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;getAssertedType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(node&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;Expression)&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;: Type &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;undefined &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isAsExpression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) || &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isTypeAssertionExpression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;)) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;return &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;checker&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getTypeAtLocation&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;ts&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;isParenthesizedExpression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;)) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;return &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getAssertedType&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;expression&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// ...
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;assembly&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#assembly&quot;&gt;How It Fits Together&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The inference hooks into &lt;code&gt;getSemanticDiagnostics&lt;/code&gt;. Every time the editor updates diagnostics, the plugin walks the AST, collects &lt;code&gt;state&lt;/code&gt; assignments, and generates the &lt;code&gt;State&lt;/code&gt; type string. If it changed since last time, &lt;code&gt;membrane.d.ts&lt;/code&gt; gets regenerated.&lt;/p&gt;
&lt;p&gt;If a developer exports their own &lt;code&gt;State&lt;/code&gt; type:&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;export &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;interface State &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  count&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  apiKey&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The plugin detects that and imports it instead of generating one. Explicit always wins.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;general&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#general&quot;&gt;The General Problem&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The technique here isn’t specific to state. It’s a general approach: infer the type of an untyped object by walking all the places it’s assigned to across a codebase. You could apply the same pattern to config objects, environment variables, feature flags, or any global singleton that accumulates properties over time.&lt;/p&gt;
&lt;p&gt;TypeScript itself has &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/34738&quot;&gt;open issues&lt;/a&gt; requesting “infer type from usage” for implicit &lt;code&gt;any&lt;/code&gt; parameters, but it’s scoped to a single function body, not whole-program analysis. &lt;a href=&quot;https://flow.org/&quot;&gt;Flow&lt;/a&gt; does whole-program type inference, but that’s an entire type system. &lt;a href=&quot;https://github.com/JoshuaKGoldberg/TypeStat&quot;&gt;TypeStat&lt;/a&gt; infers types from usage patterns and writes annotations back into source files, but it targets individual variables and parameters, not a shared object.&lt;/p&gt;
&lt;p&gt;The architecture closest to ours is &lt;a href=&quot;https://github.com/vuejs/language-tools&quot;&gt;Volar&lt;/a&gt; (Vue Language Tools), which generates virtual TypeScript files from &lt;code&gt;.vue&lt;/code&gt; single-file components. The pattern is the same: take a non-standard source of type information, generate a &lt;code&gt;.d.ts&lt;/code&gt; that the TypeScript language service can understand, and keep it updated as the source changes.&lt;/p&gt;
&lt;p&gt;We’re doing a narrower version of that. Walk assignments to one object, build a type from what we find, inject it into a generated declaration file.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;takeaways&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#takeaways&quot;&gt;What I Learned&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Working on this gave me a better feel for &lt;a href=&quot;https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API&quot;&gt;TypeScript’s compiler API&lt;/a&gt; than anything else I’ve done. The &lt;code&gt;TypeChecker&lt;/code&gt; is powerful but not always intuitive. &lt;code&gt;getTypeAtLocation&lt;/code&gt; can return &lt;code&gt;any&lt;/code&gt; in cases where you’d expect something more specific, especially when the thing you’re checking is a property of an untyped object (which &lt;code&gt;state&lt;/code&gt; is, until we infer its type). The &lt;code&gt;getBaseTypeOfLiteralType&lt;/code&gt; call is important too. Without it, &lt;code&gt;state.count = 0&lt;/code&gt; would infer the literal type &lt;code&gt;0&lt;/code&gt; instead of &lt;code&gt;number&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The biggest takeaway: type inference is a game of priorities! You have multiple sources of type information (direct assignments, fallback patterns, type assertions, compound assignments) and you need rules for which one wins. Getting those priority rules right is what makes the experience feel natural instead of surprising.&lt;/p&gt;
&lt;/section&gt;
&lt;section role=&quot;doc-endnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;Abstract Syntax Tree&lt;/a&gt;, a tree representation of source code structure.&lt;a href=&quot;#fnref1&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;typescript&lt;/code&gt; &lt;code&gt;membrane&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/onboarding/&quot;&gt;How To Onboard&lt;/a&gt; · &lt;a href=&quot;https://www.tseeley.com/posts/hello-membrane/&quot;&gt;Hello, Membrane&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>Hello, Membrane</title><id>https://www.tseeley.com/posts/hello-membrane/</id><updated>2024-11-10T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/hello-membrane/" rel="alternate" type="text/html"/><published>2024-11-10T00:00:00+00:00</published><content type="html">
&lt;p&gt;It’s been a busy, exciting past few months. I joined &lt;a href=&quot;http://membrane.io/&quot;&gt;membrane.io&lt;/a&gt; as an intern in September, and I’m having a great time making improvements to the platform and building things in user space.&lt;/p&gt;
&lt;section id=&quot;Whats-Membrane&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Whats-Membrane&quot;&gt;What’s Membrane?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Membrane is a serverless platform for developing JavaScript/TypeScript programs.&lt;/p&gt;
&lt;p&gt;Every program you create in your Membrane workspace becomes part of your graph, and the programs can connect to each other through this graph.&lt;/p&gt;
&lt;video controls width=&quot;100%&quot; src=&quot;https://docs.membrane.io/videos/add-connections.mp4#t=0.1&quot;&gt;&lt;/video&gt;
&lt;p&gt;There are a few key things that set Membrane apart from other serverless platforms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Programs have built-in state - no need to set up a database
&lt;/li&gt;
&lt;li&gt;
Everything gets logged before it happens - if it’s not in the logs, it didn’t happen
&lt;/li&gt;
&lt;li&gt;
Each program gets an email address - handle incoming emails by exporting an &lt;code&gt;email&lt;/code&gt; function
&lt;/li&gt;
&lt;li&gt;
Each program gets its own endpoint - handle HTTP requests by exporting an &lt;code&gt;endpoint&lt;/code&gt; function
&lt;/li&gt;
&lt;li&gt;
Programs can use other programs as building blocks through graph connections
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;What-Ive-Been-Working-On&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#What-Ive-Been-Working-On&quot;&gt;What I’ve Been Working On.&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most of my time has been spent on Membrane’s API drivers. Drivers let you interact with external APIs through Membrane’s graph.&lt;/p&gt;
&lt;p&gt;I’ve been improving existing drivers like &lt;a href=&quot;https://www.membrane.io/share/membrane/discord&quot;&gt;Discord&lt;/a&gt; and &lt;a href=&quot;https://www.membrane.io/share/membrane/anthropic&quot;&gt;Anthropic&lt;/a&gt; and building a &lt;a href=&quot;https://membrane.io/share/iamseeley/driver-generator&quot;&gt;tool&lt;/a&gt; to generate new ones. The more drivers on Membrane, the more useful tools and automations we can build!&lt;/p&gt;
&lt;p&gt;A driver exposes API endpoints as nodes (fields, actions, events) that you can reference in your graph with a simple syntax.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;github:users.one(name:&quot;membrane-io&quot;)&lt;/code&gt; tells Membrane to get a specific GitHub user - but here’s what’s cool: even though that API call returns all of the user’s data, you can selectively reference just the pieces you need in other programs, like if we want to get their avatar: &lt;code&gt;github:users.one(name:&quot;membrane-io&quot;).avatar_url&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Other Projects and Improvements:&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Implemented type inference for Membrane &lt;code&gt;state&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
Dynamic OG images for the changelog, packages, and docs
&lt;/li&gt;
&lt;li&gt;
Made a driver for the Membrane API: &lt;a href=&quot;https://www.membrane.io/share/iamseeley/mem&quot;&gt;mem&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m excited to start building more drivers and example tools/automations :).&lt;/p&gt;
&lt;p&gt;If you’d like to learn more about Membrane check these out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://notes.just-be.dev/Membrane%2C+A+Living+Computational+Environment&quot;&gt;Justin Bennett: Membrane, A Living Computational Environment&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://www.devtools.fm/episode/46&quot;&gt;Juan Campa - devtoolsFM episode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://www.membrane.io/blog/changelog-0.9&quot;&gt;Membrane Changelog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://docs.membrane.io/getting-started/intro/&quot;&gt;Membrane Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And join the &lt;a href=&quot;https://discord.gg/gBK9xP3z&quot;&gt;Discord&lt;/a&gt;!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;Want-to-Help-Shape-Membrane&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Want-to-Help-Shape-Membrane&quot;&gt;Want to Help Shape Membrane?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’re looking for developers to try out Membrane and share their feedback!&lt;/p&gt;
&lt;p&gt;If you’re interested in participating in user interviews, reach out to me at &lt;a href=&quot;mailto:thomas@membrane.io&quot;&gt;thomas@membrane.io&lt;/a&gt;. We’d love to hear your thoughts and help you get started building tools for yourself or for your company.&lt;/p&gt;
&lt;aside&gt;Be on the lookout for some updates coming to Membrane...soon you&apos;ll be able to create UIs with the programs on your graph!&lt;/aside&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;membrane&lt;/code&gt; &lt;code&gt;javascript&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/inferring-state-types/&quot;&gt;Inferring types from usage&lt;/a&gt; · &lt;a href=&quot;https://www.tseeley.com/posts/nvim/&quot;&gt;Neovim Cheatsheet&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>Neovim Cheatsheet</title><id>https://www.tseeley.com/posts/nvim/</id><updated>2024-10-15T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/nvim/" rel="alternate" type="text/html"/><published>2024-10-15T00:00:00+00:00</published><content type="html">
&lt;section id=&quot;Basic-Navigation&quot;&gt;
&lt;h1&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Basic-Navigation&quot;&gt;Basic Navigation&lt;/a&gt;&lt;/h1&gt;
&lt;section id=&quot;Movement&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Movement&quot;&gt;Movement&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;h&lt;/code&gt; &lt;code&gt;j&lt;/code&gt; &lt;code&gt;k&lt;/code&gt; &lt;code&gt;l&lt;/code&gt; - Left, Down, Up, Right
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;w&lt;/code&gt; - Next word start
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b&lt;/code&gt; - Previous word start
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e&lt;/code&gt; - End of word
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; - Start of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^&lt;/code&gt; - First non-blank character
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$&lt;/code&gt; - End of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gg&lt;/code&gt; - Start of file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;G&lt;/code&gt; - End of file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{number}G&lt;/code&gt; - Go to line number
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%&lt;/code&gt; - Jump to matching bracket
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Scrolling&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Scrolling&quot;&gt;Scrolling&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-d&lt;/code&gt; - Half page down
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-u&lt;/code&gt; - Half page up
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-f&lt;/code&gt; - Page down
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-b&lt;/code&gt; - Page up
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zz&lt;/code&gt; - Center cursor on screen
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Editing&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Editing&quot;&gt;Editing&lt;/a&gt;&lt;/h2&gt;
&lt;section id=&quot;Insert-Mode&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Insert-Mode&quot;&gt;Insert Mode&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;i&lt;/code&gt; - Insert before cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;I&lt;/code&gt; - Insert at start of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt; - Append after cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;A&lt;/code&gt; - Append at end of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;o&lt;/code&gt; - Open new line below
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;O&lt;/code&gt; - Open new line above
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Esc&lt;/code&gt; - Exit insert mode
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Deletion&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Deletion&quot;&gt;Deletion&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x&lt;/code&gt; - Delete character under cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd&lt;/code&gt; - Delete &lt;a href=&quot;line&quot;&gt;line&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dw&lt;/code&gt; - Delete word
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;d$&lt;/code&gt; - Delete to end of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;d0&lt;/code&gt; - Delete to start of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{number}dd&lt;/code&gt; - Delete N lines
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;CopyPaste&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#CopyPaste&quot;&gt;Copy/Paste&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;yy&lt;/code&gt; - Yank (copy) line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yw&lt;/code&gt; - Yank word
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;y$&lt;/code&gt; - Yank to end of line
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt; - Paste after cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;P&lt;/code&gt; - Paste before cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&quot;+y&lt;/code&gt; - Copy to system clipboard
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&quot;+p&lt;/code&gt; - Paste from system clipboard
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;UndoRedo&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#UndoRedo&quot;&gt;Undo/Redo&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;u&lt;/code&gt; - Undo
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-r&lt;/code&gt; - Redo
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt; - Repeat last command
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Visual-Mode&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Visual-Mode&quot;&gt;Visual Mode&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;v&lt;/code&gt; - Character-wise visual
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;V&lt;/code&gt; - Line-wise visual
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-v&lt;/code&gt; - Block visual
&lt;/li&gt;
&lt;li&gt;
Select then &lt;code&gt;d&lt;/code&gt; to delete, &lt;code&gt;y&lt;/code&gt; to copy
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;Search-Replace&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Search-Replace&quot;&gt;Search &amp;amp; Replace&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/pattern&lt;/code&gt; - Search forward
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?pattern&lt;/code&gt; - Search backward
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; - Next match
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;N&lt;/code&gt; - Previous match
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; - Search word under cursor
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:%s/old/new/g&lt;/code&gt; - Replace all in file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:%s/old/new/gc&lt;/code&gt; - Replace with confirmation
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;File-Operations&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#File-Operations&quot;&gt;File Operations&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:w&lt;/code&gt; - Save file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:wq&lt;/code&gt; or &lt;code&gt;:x&lt;/code&gt; - Save and quit
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:q&lt;/code&gt; - Quit
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:q!&lt;/code&gt; - Quit without saving
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:e filename&lt;/code&gt; - Open file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:bn&lt;/code&gt; - Next buffer
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:bp&lt;/code&gt; - Previous buffer
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;My-Custom-Keymaps&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#My-Custom-Keymaps&quot;&gt;My Custom Keymaps&lt;/a&gt;&lt;/h2&gt;
&lt;section id=&quot;Leader-Key-Space&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Leader-Key-Space&quot;&gt;Leader Key: &lt;code&gt;&amp;lt;Space&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;/section&gt;
&lt;section id=&quot;File-Navigation&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#File-Navigation&quot;&gt;File Navigation&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-&lt;/code&gt; - Open oil.nvim file explorer
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;ff&lt;/code&gt; - Find files (Telescope)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;fg&lt;/code&gt; - Live grep (search in files)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;fb&lt;/code&gt; - List buffers
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;fh&lt;/code&gt; - Help tags
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Wiki-Navigation&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Wiki-Navigation&quot;&gt;Wiki Navigation&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;ni&lt;/code&gt; - Wiki index
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;nd&lt;/code&gt; - Today’s daily note
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;nD&lt;/code&gt; - Daily notes index
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;nw&lt;/code&gt; - Weekly notes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;nm&lt;/code&gt; - Monthly notes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;ny&lt;/code&gt; - Yearly notes
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;VimWiki-Specific&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#VimWiki-Specific&quot;&gt;VimWiki Specific&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Enter&amp;gt;&lt;/code&gt; - Follow/create wiki link
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Backspace&amp;gt;&lt;/code&gt; - Go back
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; - Next link
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Shift-Tab&amp;gt;&lt;/code&gt; - Previous link
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Ctrl-Space&amp;gt;&lt;/code&gt; - Toggle task checkbox
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;LSP&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#LSP&quot;&gt;LSP&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;K&lt;/code&gt; - Hover documentation
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gd&lt;/code&gt; - Go to definition
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gr&lt;/code&gt; - Find references
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Ctrl-Space&amp;gt;&lt;/code&gt; - Trigger completion
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;CR&amp;gt;&lt;/code&gt; - Confirm completion
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Oilnvim-File-Explorer&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Oilnvim-File-Explorer&quot;&gt;Oil.nvim File Explorer&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-&lt;/code&gt; - Open parent directory
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Enter&amp;gt;&lt;/code&gt; - Open file/directory
&lt;/li&gt;
&lt;li&gt;
Edit text to rename
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd&lt;/code&gt; - Delete file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;o&lt;/code&gt; - Create new file
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:w&lt;/code&gt; - Save changes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt; - Close oil
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;Window-Management&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Window-Management&quot;&gt;Window Management&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:split&lt;/code&gt; or &lt;code&gt;:sp&lt;/code&gt; - Horizontal split
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:vsplit&lt;/code&gt; or &lt;code&gt;:vsp&lt;/code&gt; - Vertical split
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-w h/j/k/l&lt;/code&gt; - Navigate between windows
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-w =&lt;/code&gt; - Equal window sizes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-w q&lt;/code&gt; - Close window
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Tips-Tricks&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Tips-Tricks&quot;&gt;Tips &amp;amp; Tricks&lt;/a&gt;&lt;/h2&gt;
&lt;section id=&quot;Multiple-Cursors-Visual-Block&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Multiple-Cursors-Visual-Block&quot;&gt;Multiple Cursors (Visual Block)&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-v&lt;/code&gt; - Start visual block
&lt;/li&gt;
&lt;li&gt;
Select lines with &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;I&lt;/code&gt; - Insert at start of all lines
&lt;/li&gt;
&lt;li&gt;
Type text, press &lt;code&gt;Esc&lt;/code&gt; twice
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id=&quot;Macros&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Macros&quot;&gt;Macros&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;q{letter}&lt;/code&gt; - Start recording macro
&lt;/li&gt;
&lt;li&gt;
Do actions
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt; - Stop recording
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@{letter}&lt;/code&gt; - Replay macro
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@@&lt;/code&gt; - Replay last macro
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Marks&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Marks&quot;&gt;Marks&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;m{letter}&lt;/code&gt; - Set mark
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&apos;{letter}&lt;/code&gt; - Jump to mark
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&apos;&apos;&lt;/code&gt; - Jump back to previous position
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Command-Line&quot;&gt;
&lt;h3&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Command-Line&quot;&gt;Command Line&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:!command&lt;/code&gt; - Run shell command
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:r !command&lt;/code&gt; - Insert command output
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:%!command&lt;/code&gt; - Filter file through command
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;VimWiki-Tasks&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#VimWiki-Tasks&quot;&gt;VimWiki Tasks&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;- [ ]&lt;/code&gt; - Create task
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-Space&lt;/code&gt; - Toggle task state
- &lt;code&gt;[ ]&lt;/code&gt; - Not done
- &lt;code&gt;[.]&lt;/code&gt; - 0-33% done
- &lt;code&gt;[o]&lt;/code&gt; - 34-66% done
- &lt;code&gt;[O]&lt;/code&gt; - 67-99% done
- &lt;code&gt;[X]&lt;/code&gt; - Done
&lt;/li&gt;
&lt;li&gt;
Nested tasks auto-update parent status
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;Resources&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Resources&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:help&lt;/code&gt; - Neovim help
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:help {topic}&lt;/code&gt; - Help on specific topic
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:Tutor&lt;/code&gt; - Interactive Neovim tutorial
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;cheatsheet&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/hello-membrane/&quot;&gt;Hello, Membrane&lt;/a&gt; · &lt;a href=&quot;https://www.tseeley.com/posts/maze-playground/&quot;&gt;Maze Playground&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>Maze Playground</title><id>https://www.tseeley.com/posts/maze-playground/</id><updated>2024-08-22T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/maze-playground/" rel="alternate" type="text/html"/><published>2024-08-22T00:00:00+00:00</published><content type="html">
&lt;p&gt;I read Andrew Healey’s &lt;a href=&quot;https://healeycodes.com/generating-mazes&quot;&gt;Generating Mazes&lt;/a&gt; and wanted to see the algorithms work. Not just the finished maze, but each step of the generation. Walls getting removed one at a time, the path carving through the grid.&lt;/p&gt;
&lt;p&gt;So I built a &lt;a href=&quot;https://github.com/iamseeley/maze-playground&quot;&gt;small playground&lt;/a&gt; for it. Deno, TypeScript, and a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;section id=&quot;The-Data-Model&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#The-Data-Model&quot;&gt;The Data Model&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A maze is a grid of cells. Each cell has four walls and knows whether it’s been visited.&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;export &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;class Cell implements CellInterface &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;{
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;Position&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  hasTopWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  hasRightWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  hasBottomWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  hasLeftWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  visited&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  removeWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;neighbor: CellInterface&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;void {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    const dx = neighbor.position.x - this.position.x;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    const dy = neighbor.position.y - this.position.y;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;if &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(dx === &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;this.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;hasRightWall &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      neighbor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;hasLeftWall &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#666666;&quot;&gt;// ... and so on for each direction
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;removeWall&lt;/code&gt; figures out which direction the neighbor is in and removes the wall on both sides. That’s the core operation of maze generation: two cells become connected.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;Generators-for-Stepping&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Generators-for-Stepping&quot;&gt;Generators for Stepping&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The part I liked most was using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators&quot;&gt;JavaScript generators&lt;/a&gt; for the algorithms. DFS yields after each step, which means the UI can consume it at whatever pace it wants.&lt;/p&gt;
&lt;pre data-lang=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#888888;&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;generateSteps&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;maze: MazeInterface&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;startCell: CellInterface&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;: Generator&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;lt;{ &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;currentCell&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;CellInterface &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}&amp;gt; {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  const stack&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;CellInterface&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;[] = [&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;startCell&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  startCell.visited = true;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;while &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(stack&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt; &amp;gt; 0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;currentCell &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;stack&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;stack&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;length &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;];
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;yield { &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;currentCell &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;};
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;unvisitedNeighbors &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;maze&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;getNeighbors&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;currentCell&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(neighbor =&amp;gt; !&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;neighbor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;visited&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;if (&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;unvisitedNeighbors&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;length &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;) {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;randomIndex &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;floor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;Math&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;random&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;() * &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;unvisitedNeighbors&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;chosenNeighbor &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;unvisitedNeighbors&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;randomIndex&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;];
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      currentCell&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;removeWall&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;chosenNeighbor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      chosenNeighbor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;visited &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;;
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      stack&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;chosenNeighbor&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;);
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;} else {
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;      stack&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;pop&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;();
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI runs a &lt;code&gt;setInterval&lt;/code&gt; at 50ms that calls &lt;code&gt;generator.next()&lt;/code&gt;, snapshots the maze state, and renders it. After generation is done, a slider lets you scrub back through every step. You can watch the algorithm think.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;Whats-There-Whats-Not&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#Whats-There-Whats-Not&quot;&gt;What’s There, What’s Not&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Right now only &lt;a href=&quot;https://en.wikipedia.org/wiki/Depth-first_search&quot;&gt;DFS&lt;/a&gt; is implemented. The dropdown lists &lt;a href=&quot;https://en.wikipedia.org/wiki/Maze_generation_algorithm#Aldous-Broder_algorithm&quot;&gt;Aldous-Broder&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Prim&apos;s_algorithm&quot;&gt;Prim’s&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Kruskal&apos;s_algorithm&quot;&gt;Kruskal’s&lt;/a&gt; but they’re stubs. I’d like to add them eventually because the interesting thing about maze algorithms is how different the mazes &lt;em&gt;feel&lt;/em&gt;. DFS creates long winding corridors with few branches. Aldous-Broder creates more uniform, random-looking mazes. Same grid, different character.&lt;/p&gt;
&lt;p&gt;The rendering is simple Canvas drawing. Visited cells get a gray fill, the current cell gets a blue highlight, and walls are just lines. Nothing fancy, but it’s enough to see what’s happening.&lt;/p&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;typescript&lt;/code&gt; &lt;code&gt;algorithms&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/nvim/&quot;&gt;Neovim Cheatsheet&lt;/a&gt; · &lt;a href=&quot;https://www.tseeley.com/posts/schemeing/&quot;&gt;Scheme-ing&lt;/a&gt; &amp;rarr;&lt;/p&gt;
</content></entry><entry><title>Scheme-ing</title><id>https://www.tseeley.com/posts/schemeing/</id><updated>2024-07-04T00:00:00+00:00</updated><author><name>Thomas Seeley</name></author><link href="https://www.tseeley.com/posts/schemeing/" rel="alternate" type="text/html"/><published>2024-07-04T00:00:00+00:00</published><content type="html">
&lt;p&gt;I’ve been reading &lt;a href=&quot;https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html&quot;&gt;SICP&lt;/a&gt; on and off. To be honest, I’ve only made it a few chapters in and I think that’s as far as I’ll go.&lt;/p&gt;
&lt;p&gt;Around the same time I read &lt;a href=&quot;https://bookshop.org/books/wolf-in-white-van/9781250074713&quot;&gt;&lt;em&gt;Wolf in White Van&lt;/em&gt;&lt;/a&gt; by John Darnielle. It’s a novel about a guy who runs a &lt;a href=&quot;https://en.wikipedia.org/wiki/Interactive_fiction&quot;&gt;text-based adventure game&lt;/a&gt; by mail. Players send in their moves, he sends back what happens. The game is called Trace Italian! The whole book is told in reverse, which gives it this strange gravity. You know where the story ends up before you know how it started.&lt;/p&gt;
&lt;p&gt;I thought it would be fun to build a text adventure in Scheme. So I did!&lt;/p&gt;
&lt;section id=&quot;The-Game&quot;&gt;
&lt;h2&gt;&lt;a class=&quot;heading-anchor&quot; href=&quot;#The-Game&quot;&gt;The Game&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s small. Four rooms, directional movement, a game loop. No items, no puzzles, no win condition. Just the skeleton of a world you can walk around in.&lt;/p&gt;
&lt;pre data-lang=&quot;scheme&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;define &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;rooms
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;list
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;   (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;list &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;living-room &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;You are in the living room. There is a door to the north and a kitchen to the west.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;   (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;list &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;kitchen &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;You are in the kitchen. There is a living room to the east.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;   (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;list &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;hallway &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;You are in the hallway. There is a living room to the south and a bedroom to the north.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;   (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;list &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;bedroom &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;You are in the bedroom. There is a hallway to the south.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rooms are lists. Connections between rooms are lists. The game state is which room you’re in.&lt;/p&gt;
&lt;pre data-lang=&quot;scheme&quot;&gt;&lt;code&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;define &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;(game-loop)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  (describe-current-room)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;((command (get-command)))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;cond &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;((eq&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;? &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;command &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;north) (move &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;north))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;          ((eq&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;? &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;command &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;south) (move &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;south))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;          ((eq&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;? &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;command &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;east) (move &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;east))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;          ((eq&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;? &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;command &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;west) (move &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;west))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;          ((eq&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;? &lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;command &lt;/span&gt;&lt;span style=&quot;color:#e0c020;&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;quit) (display &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;Goodbye!&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;                                (&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;newline&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;                                (exit))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;          (else (display &lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-style:italic;color:#e0c020;&quot;&gt;Invalid command.&lt;/span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;                (&lt;/span&gt;&lt;span style=&quot;font-weight:bold;color:#eeeeee;&quot;&gt;newline&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;)))
&lt;/span&gt;&lt;span style=&quot;color:#e2e2e5;&quot;&gt;    (game-loop)))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The game loop is a recursive function that describes where you are, reads a command, acts on it, then calls itself. No &lt;code&gt;while&lt;/code&gt; loop, no mutable iterator. Just a function that calls itself forever until you quit.&lt;/p&gt;
&lt;p&gt;It’s a good reminder that you don’t need much to make something interactive. Four rooms and a prompt. That’s a world you can walk around in.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/iamseeley/just-scheme-things/blob/main/adventure.scm&quot;&gt;full source&lt;/a&gt; is about 60 lines. There’s also a &lt;a href=&quot;https://github.com/iamseeley/just-scheme-things/blob/main/tic-tac-toe.scm&quot;&gt;tic-tac-toe&lt;/a&gt; in the same repo from the same weekend.&lt;/p&gt;
&lt;/section&gt;

&lt;hr&gt;
&lt;p&gt;&lt;code&gt;scheme&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tseeley.com/about/&quot;&gt;Thomas Seeley&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/tseeley.com&quot;&gt;@tseeley.com on Bluesky&lt;/a&gt;, &lt;a href=&quot;https://mastodon.social/@iamseeley&quot;&gt;@iamseeley on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;larr; &lt;a href=&quot;https://www.tseeley.com/posts/maze-playground/&quot;&gt;Maze Playground&lt;/a&gt;&lt;/p&gt;
</content></entry></feed>