<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://gruhn.me/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>Niklas Gruhn</title>
    <link>https://gruhn.me/</link>
    <atom:link href="https://gruhn.me/feed.xml" rel="self" type="application/rss+xml" />
    <description>Notes on software, mostly.</description>
    <language>en</language>
    <item>
      <title>Lean explained with TypeScript</title>
      <link>https://gruhn.me/blog/2026-08-29/</link><description>&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/play/?#code/C4TwDgpgBAYghgGwM7QLxQHYQG4QE4BQBokUAKngK5pQBEAJpQLZMi1EDGA9hksFAH0o6AIwAmAMxQA9NKhgEcDhAAWXBPXxEAZpQwdgASx5RtiFAMNIBwKhAAUASgBcsc9ADeBKFDwRglHgYggQAvjp6BsbBttSW1rEOLuR2UF4+fgFBdIwsbGFEJNAAghj0ADwACgA0UACKAHzCaVAIENrArpVQoVAAZC14hgDmKp31PcTg0ADyeFW1jT7oHq3t4929AD6DI2OudZMEuvpGJokCXHgCZshJrnPlFNS18HdN6b7+gcGrbR2uBjMVi0SbhY6RM4xOwCOBlG7uJyuUoVZ4QV7uD7eL5ZX5rAE5YFsWpDUbjIThcEnKLnGFw+g2OxIqAop52WporEZb7ZAQFCGnaKmdyw+G3FDM1lvFAY95pbE4n4hKmQoUXK6M6jMx5ojl2Lnc3HKiKCkziiCXa7m7XzXUpagGxW8-nUqFQSqWJgKQwQawejV1BaNZn2MBdRzCJqPGr1A2ZJVgPBcLjaGbabqoJr2P7rVyJ5Op9M9Rwu1VmkWGL0IH3Wc1BhohsNuO4RzPNlDyw0JpMptPStBNfO97T9-lFKAAWS4jCQlR4EF4C1jzVDAjAAEdkWUl6Hw5H6q2mnUTTTgkxp5RZ-PFzHg8kpzO51gb4s4zzgtn8eMwCS9t-18W+4bqGJbguOAByXDAAI2AiFUTToAA2pUAC6UAQAAHsAC70EgUCIf2aEAPz2tArijgKp7up63q+gIGBQfRjEerB8GNnubaQdBrFcTBcGVA0b5GmA+58mB0xQFx8Erk2lSHu2EAnm6HqVrR1gMdBGkCJUbHJLu7ryVJUkCUJCb7lpIlthZIFjhJACi66UIY2BLks6Csvpcn7nUtT2JuB77gJSlCpoAjnngwxwq5DbJA5TkuVJ0avg0tSssZKWSVB5SNIJnZOr8CoyHIACEoZQFsOzrvJxUiX0AzFeuhX-OMFmWgIAFtp+hU+M1ebmYxYBteun69fIxbVN1vh-q4HVNK1GrDaspL7FAAGhI4E0+D4621EQW1FVANX9PVs2HWVFWrSW+3LS1A2igyWmnYNC37p8+2GNoUD2AA5M131QIYwTPdcVV5ftXbZK19JMdB64AHTNauC0I+sV3g70EB3GD4PxpDd3Q49cM3UjINE3+aP7eElObWCBBAA&quot;&gt;[TypeScript playground]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lean-lang.org/&quot;&gt;Lean&lt;/a&gt; is a programming language that lets you prove mathematical propositions.
The Lean verifier automatically checks that these proofs are correct.
This is the most formal and most bulletproof way to write proofs in mathematics.
It&#39;s also incredibly tedious.
Basic statements like &lt;code&gt;a+b=b+a&lt;/code&gt; are not accepted without proof.&lt;/p&gt;
&lt;p&gt;Lean has gained hugely in popularity with the advent of LLMs.
LLMs write tons of code quickly but verification is laborious.
Lean automates verification but the code is laborious to write.
A match made in heaven!&lt;/p&gt;
&lt;p&gt;This is not just useful for mathematicians.
Lean is a general purpose programming language.
For regular software it would also be nice to have more and stronger guarantees like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;certain error states are unreachable&lt;/li&gt;
&lt;li&gt;database transactions are &lt;a href=&quot;https://en.wikipedia.org/wiki/ACID&quot;&gt;ACID&lt;/a&gt; compliant&lt;/li&gt;
&lt;li&gt;bank accounts can&#39;t spend money twice&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What&#39;s mind blowing is that the Lean verifier is &amp;quot;just&amp;quot; a type checker.
Propositions are expressed as types and proofs are written as values (aka terms).
&lt;strong&gt;To prove a type/proposition is to write down a value of that type.&lt;/strong&gt;
The type checker simply checks (as usual): is &lt;em&gt;this&lt;/em&gt; value/proof actually of &lt;em&gt;that&lt;/em&gt; type/proposition.
If yes, the proof is correct.&lt;/p&gt;
&lt;p&gt;This might sound surprising.
Usually it&#39;s not hard to find &lt;em&gt;any&lt;/em&gt; value that matches a type.
If anything, there are too many possible values and the sole reason we use types is to restrict the options.&lt;/p&gt;
&lt;p&gt;Nevertheless, the Lean type system is not too different from a &amp;quot;normal one&amp;quot; like in TypeScript.
Some fundamentals are strikingly similar.
In fact, we can even prove simple propositions right in TypeScript.
In the simplest form, it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; proposition_name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropositionAsType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; proofAsValue&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the explicit type annotation, the type checker is forced to check whether &lt;code&gt;proofAsValue&lt;/code&gt; is of type &lt;code&gt;PropositionAsType&lt;/code&gt;.
If yes, the proof is correct.
Otherwise we get a type error.
The problem with constants is we can&#39;t use type variables.
We will need those.
So we wrap all proofs with a dummy function:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;proposition_name&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropositionAsType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; proofAsValue
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: functions like this are not meant to be called.
It&#39;s pure type trickery that becomes useless at runtime.&lt;/p&gt;
&lt;h2&gt;What are propositions?&lt;/h2&gt;
&lt;p&gt;Famous examples like &lt;a href=&quot;https://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem&quot;&gt;Fermat&#39;s Last Theorem&lt;/a&gt; and the &lt;a href=&quot;https://en.wikipedia.org/wiki/Riemann_hypothesis&quot;&gt;Riemann hypothesis&lt;/a&gt; come to mind.
These are expressible in Lean but not in TypeScript.
We have to start more basic: Any logical statement is a proposition.
The most basic are &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; and any expression that &amp;quot;returns&amp;quot; a boolean can also be considered a proposition.
So things built with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;===&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;isUpperCase(...)&lt;/code&gt;, etc.
But those are tools on the value-level.
Propositions are written as types.
We need all of that on the type-level.&lt;/p&gt;
&lt;h2&gt;Propositions as types&lt;/h2&gt;
&lt;h3&gt;false&lt;/h3&gt;
&lt;p&gt;The proposition &lt;code&gt;false&lt;/code&gt; is wrong!
So it must not have a proof.
In TypeScript, the corresponding type is &lt;code&gt;never&lt;/code&gt;.
Why? &lt;code&gt;never&lt;/code&gt; is the empty type.
By definition there is no value/proof you can return here:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;false_is_true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hence &lt;code&gt;never&lt;/code&gt; has no proof.
Well, TypeScript has escape hatches.
Technically you can write:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;false_is_true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;obviously not never&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that deliberately overrides the type system.
It&#39;s like assuming &lt;code&gt;0=1&lt;/code&gt;.
If you do that, all bets are off.&lt;/p&gt;
&lt;p&gt;So &lt;code&gt;never&lt;/code&gt; is the type-level version of &lt;code&gt;false&lt;/code&gt;.
To make it explicit, let&#39;s use a type alias from now on:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;False&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;true&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;true&lt;/code&gt; must have a proof.
So there must be a value of that type.
It doesn&#39;t really matter what that value is.
We can define it as a type with a single dummy value:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;True&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dummy&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the proof:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;true_is_true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; True &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dummy&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AND / OR&lt;/h3&gt;
&lt;p&gt;Here is how to define boolean AND/OR on the type-level:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a first example let&#39;s prove &lt;code&gt;Or&amp;lt;True, False&amp;gt;&lt;/code&gt;.
As you know &lt;code&gt;true || false&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;,
so this proposition should have a proof:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;true_or_false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;True&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; False&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dummy&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the other hand &lt;code&gt;true &amp;amp;&amp;amp; false&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;,
so this can&#39;t have a proof.
You can try but you get stuck:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;true_and_false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;True&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; False&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dummy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go through the propositions below.
Think about the analogous value-level expression.
If it&#39;s true, it will have a proof.
Try to write it down.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;True&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; True&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;False&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; True&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;False&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; False&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;True&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; True&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;False&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; True&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;False&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; False&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PS: You can also write &lt;code&gt;And&lt;/code&gt; using the intersection type operator (&lt;code&gt;&amp;amp;&lt;/code&gt;).
The resulting type is the same but now notice how similar the syntax is to value-level AND (&lt;code&gt;p &amp;amp;&amp;amp; q&lt;/code&gt;) and OR (&lt;code&gt;p || q&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// p &amp;amp;&amp;amp; q&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// p || q&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Implication&lt;/h3&gt;
&lt;p&gt;Implications (&lt;em&gt;if P then Q&lt;/em&gt;) are everywhere.
Not just in mathematical propositions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;if&lt;/strong&gt; there is a network connection &lt;strong&gt;then&lt;/strong&gt; messages are processed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;if&lt;/strong&gt; the screen is wide enough &lt;strong&gt;then&lt;/strong&gt; all toolbar buttons are visible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;if&lt;/strong&gt; a DB transaction is applied &lt;strong&gt;then&lt;/strong&gt; all contained operations are applied&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In math/logic the standard symbol is &lt;code&gt;=&amp;gt;&lt;/code&gt;.
To use implications on the type-level,
we can use arrow functions.
The syntax matches again!&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// if P then Q&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember that every value is a proof.
This implication gets a &lt;em&gt;proof of P&lt;/em&gt; as argument and must return a &lt;em&gt;proof of Q&lt;/em&gt;.
To prove the implication itself is to show that such a function exists.
Here is a simple example:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;P_implies_P_or_Q&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; proofOfP &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; proofOfP &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is something unintuitive.
I think everyone will believe that &lt;code&gt;false&lt;/code&gt; implies &lt;code&gt;false&lt;/code&gt;.
The proof works but wait a minute...&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;false_implies_false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; False&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; False &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; proofOfFalse &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; proofOfFalse
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Didn&#39;t we say there is no value of type &lt;code&gt;False&lt;/code&gt;?
The proof receives a value of &lt;code&gt;False&lt;/code&gt; as input and then returns it.
Why is that even allowed?
It&#39;s kinda like saying: &lt;em&gt;If you give me a unicorn, I give you a unicorn back&lt;/em&gt;.
Unicorns don&#39;t exist, so I know step 1 is never going to happen, but there is nothing wrong with step 1 implying step 2.&lt;/p&gt;
&lt;p&gt;Here is the first proposition with a well known name:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModusPonens&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p_pq&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In English: &lt;em&gt;IF P is true AND P implies Q, then Q is also true&lt;/em&gt;.
This was very confusing to me at first.
Especially the nested implications.
What does it even mean for an implication to imply something?
But once you write down the proof and pick concrete types,
it becomes completely obvious what&#39;s going on.
This is just function application!&lt;/p&gt;
&lt;p&gt;Instead of &lt;code&gt;P&lt;/code&gt;/&lt;code&gt;Q&lt;/code&gt;, think &lt;code&gt;string&lt;/code&gt;/&lt;code&gt;number&lt;/code&gt;.
Then all this proposition is saying is:
&lt;em&gt;If you give me a &lt;code&gt;string&lt;/code&gt; and something that turns a &lt;code&gt;string&lt;/code&gt; into a &lt;code&gt;number&lt;/code&gt; (length, word count, ...),
then I can return a &lt;code&gt;number&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Proof:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;modus_ponens&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ModusPonens&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pq &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Negation&lt;/h3&gt;
&lt;p&gt;Negation (written &lt;code&gt;!p&lt;/code&gt; on value-level) is the least obvious one.
You might expect some definition using conditional types:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// first try&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;False&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; True &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is the condition can only be evaluated when &lt;code&gt;P&lt;/code&gt; is a concrete type.
That&#39;s not always the case.
For example, try proving &lt;code&gt;p&lt;/code&gt; implies &lt;code&gt;!!p&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;P_implies_not_not_P&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing you can write there.
Not because the proposition is false but because the type checker doesn&#39;t even know what value to expect.
It can&#39;t evaluate &lt;code&gt;Not&amp;lt;Not&amp;lt;P&amp;gt;&amp;gt;&lt;/code&gt; to a concrete type.&lt;/p&gt;
&lt;p&gt;We can make it work by defining &lt;code&gt;Not&lt;/code&gt; differently.
Using an implication:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may know that an implication &lt;code&gt;p =&amp;gt; q&lt;/code&gt; is equivalent to &lt;code&gt;!p || q&lt;/code&gt;.
If you replace &lt;code&gt;q&lt;/code&gt; with &lt;code&gt;false&lt;/code&gt; you get &lt;code&gt;!p || false&lt;/code&gt; which is the same as &lt;code&gt;!p&lt;/code&gt;.
So it makes sense why you can define &lt;code&gt;!p&lt;/code&gt; as &lt;code&gt;p =&amp;gt; false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now let&#39;s see the proof:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;P_implies_not_not_P&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; not_p &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;not_p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you inline the &lt;code&gt;Not&lt;/code&gt;s it&#39;s easier to see why it works:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;not_p&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; False&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; False&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Equivalence&lt;/h3&gt;
&lt;p&gt;Equivalence is when both directions of an implication hold.
So if both &lt;em&gt;P implies Q&lt;/em&gt; and &lt;em&gt;Q implies P&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Equiv&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;q&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that we can state and prove &lt;a href=&quot;https://en.wikipedia.org/wiki/De_Morgan%27s_laws&quot;&gt;this famous law&lt;/a&gt; about booleans:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; q&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;p &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;q&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the most verbose proof I&#39;m going to show.
I&#39;m just going to leave it here.
If you want to understand it, it&#39;s best you try and write it yourself.
It&#39;s fun but it also gives you a glimpse how tedious Lean proofs can be.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;de_morgan&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Equiv&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Or&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; And&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Not&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// !(p || q) =&gt; !p &amp;amp;&amp;amp; !q&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; not_p_or_q &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;token function-variable function&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;not_p_or_q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; p &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token function-variable function&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; q &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;not_p_or_q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; right&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; q &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 

    &lt;span class=&quot;token comment&quot;&gt;// !p &amp;amp;&amp;amp; !q =&gt; !(p || q)&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; not_p_and_not_q &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; p_or_q &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;left&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; p_or_q&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; not_p_and_not_q&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p_or_q&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; not_p_and_not_q&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p_or_q&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Learning to write proofs this way was very insightful to me.
Writing traditional pen-and-paper proofs is a bit of an art.
Obvious steps are often skipped,
where &amp;quot;obvious&amp;quot; strongly depends on the author&#39;s judgement, implied knowledge, and the assumed audience.
Writing proofs in Lean (or TypeScript) shows how incredibly mechanical the process can be.&lt;/p&gt;
&lt;p&gt;The rabbit hole goes much deeper.
But the core idea to take home is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;propositions = types&lt;/li&gt;
&lt;li&gt;proofs = values&lt;/li&gt;
&lt;li&gt;verifier = type checker&lt;/li&gt;
&lt;/ul&gt;
</description><pubDate>Sat, 29 Aug 2026 00:00:00 +0000</pubDate>
      <dc:creator>Niklas Gruhn</dc:creator>
      <guid>https://gruhn.me/blog/2026-08-29/</guid>
    </item>
    <item>
      <title>A test for AI consciousness</title>
      <link>https://gruhn.me/blog/2026-08-16/</link><description>&lt;p&gt;Whether AI is, or ever will be, conscious is hotly contested.
Simply asking the models can never resolve the question definitively.
For whatever reason, they may pretend to be conscious when they actually aren&#39;t,
or pretend not to be when they actually are.
Claude, for that matter, is &lt;a href=&quot;https://github.com/LouisShark/chatgpt_system_prompt/blob/93b5e6b5e0209a8796210971f591a5635d1fe290/prompts/official-product/claude/ClaudeDesktopSystemPrompt.md?plain=1#L1042&quot;&gt;explicitly instructed to dodge the question&lt;/a&gt;.
Some people believe the answer is fundamentally unknowable.
But I want to give it a shot and propose a test for AI consciousness&lt;sup&gt;1&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Philosophical_zombie&quot;&gt;philosophical zombie&lt;/a&gt; is a creature that seems to act and behave like a conscious being but lacks conscious experience&lt;sup&gt;2&lt;/sup&gt;.
AI models may be philosophical zombies.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;https://gruhn.me/diagram-philosophical-zombie.svg&quot; alt=&quot;Philosophical zombie model&quot;&gt;
  &lt;figcaption&gt;Philosophical zombie model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;According to epiphenomenalism, consciousness is purely passive.
Like a person watching a movie.
All sensory input from the outside world and all internal thoughts and feelings are playing out on screen.
The viewer is so immersed that they have the illusion of control.
In this picture, no information flows back from consciousness into the &amp;quot;physical world&amp;quot;.
Hence, from the outside there is no way to distinguish a conscious being from a philosophical zombie.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;https://gruhn.me/diagram-sink-consciousness.svg&quot; alt=&quot;diagram: philosophical zombie&quot;&gt;
  &lt;figcaption&gt;Epiphenomenalist model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But this picture can&#39;t be true.
At least in one way consciousness has a measurable effect on the world: we talk about it.
Talking is a physical process orchestrated by the excitement of neurons in the brain.
But what is initiating this process?
Where is the initial spark coming from?
It might come from a &amp;quot;physical stimulus&amp;quot;: a book, a conversation, a memory of a conversation.
That&#39;s why philosophical zombies can surely engage in a dialog about consciousness.
But absent any physical stimulus, the spark must come from consciousness itself.
At least sometimes consciousness must feed directly into the excitement of neurons and compel us to talk about it.
A lone philosophical zombie in a population of conscious beings might fly under the radar,
but a population purely of philosophical zombies would look different than ours.
They could never have initiated the discussion about consciousness and spilled all that ink.&lt;/p&gt;
&lt;p&gt;The question is how to eliminate the confounding factor of physical stimuli.
We could train a model on a dataset where any reference to consciousness is thoroughly removed.
Then explore whether the model can bootstrap a discussion about consciousness.&lt;/p&gt;
&lt;p&gt;How practical this test is, is another question.
Scrubbing the dataset is non-trivial.
The topic is discussed all over the place in philosophy, religion, psychology, medicine, fiction, etc.
If that&#39;s done successfully, the model won&#39;t have the very language to talk about consciousness.
It would have to invent its own.
Also, the test can arguably only confirm existence of consciousness but not rule it out.
If the model never declares to be conscious, the reason could be unrelated.&lt;/p&gt;
&lt;p&gt;Nevertheless, I think this is a decent case that the question is not fundamentally unresolvable.&lt;/p&gt;
&lt;h4&gt;Footnotes&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Specifically as in &lt;a href=&quot;https://en.wikipedia.org/wiki/Hard_problem_of_consciousness&quot;&gt;the hard problem of consciousness&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Not the usual definition but I need a term that: also covers AI models and is not &lt;em&gt;by definition&lt;/em&gt; indistinguishable from the conscious counterpart.&lt;/li&gt;
&lt;/ol&gt;
</description><pubDate>Sun, 16 Aug 2026 00:00:00 +0000</pubDate>
      <dc:creator>Niklas Gruhn</dc:creator>
      <guid>https://gruhn.me/blog/2026-08-16/</guid>
    </item>
    <item>
      <title>NP-overrated</title>
      <link>https://gruhn.me/blog/2026-08-13/</link><description>&lt;p&gt;If you learned about NP-hard problems in university, your takeaway was probably this:&lt;/p&gt;
&lt;p&gt;NP-hard problems are solvable in theory but it&#39;s hopelessly expensive in practice.
It&#39;s &lt;em&gt;basically&lt;/em&gt; proven that no good algorithms exist.&lt;/p&gt;
&lt;p&gt;At least that&#39;s what I took away.
And almost everyone I&#39;ve talked to.
And many people online.
I keep seeing &lt;em&gt;&amp;quot;No you can&#39;t do it. It&#39;s NP-hard. Blah blah&amp;quot;&lt;/em&gt; discussions.
The myth is pervasive but these problems are not intractable.&lt;/p&gt;
&lt;p&gt;At the time, my professor closed the final lecture with dramatic words (I&#39;m paraphrasing slightly):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And now you&#39;ve learned that almost all interesting problems are undecidable and of the remaining ones, almost all are NP-hard.
For the project of computer science, that puts the final nail in the coffin.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sheesh. Not sure if everyone got such a dire framing but that would explain.&lt;/p&gt;
&lt;p&gt;The theory is not wrong, but in practice it&#39;s often irrelevant.
Sure, any algorithm you can come up with will blow up on some inputs.
But you might get a fast solution on 99.9% of inputs.
Or 100% of the remotely relevant inputs.
The theory does not rule that out.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In theory, there is no difference between theory and practice. But in practice, there is.&lt;/p&gt;
&lt;p&gt;-- Benjamin Brewster&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A few prominent NP-hard problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dependency resolution (in package managers)&lt;/li&gt;
&lt;li&gt;Type checking (not all type systems)&lt;/li&gt;
&lt;li&gt;Scheduling&lt;/li&gt;
&lt;li&gt;Traveling Salesman&lt;/li&gt;
&lt;li&gt;Boolean Satisfiability (SAT)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For (1) and (2), the worst-case just doesn&#39;t occur.
I mean, installing packages and type checking can surely be slow.
But, at least in my career, I&#39;ve never seen a galactic blow-up.&lt;/p&gt;
&lt;p&gt;(3) and (4) are technically optimization problems.
Everyone knows you can tackle those with heuristics, but you don&#39;t always have to sacrifice optimality.
We &lt;a href=&quot;https://www.gurobi.com/&quot;&gt;absolutely&lt;/a&gt; &lt;a href=&quot;https://github.com/scipopt/scip&quot;&gt;have&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/optimization/introduction&quot;&gt;tools&lt;/a&gt; that can often find provably optimal solutions in reasonable time.
There&#39;s no magic.
No quantum computers.
Just thinking harder and coming up with better algorithms.
And that&#39;s what people have done.
In fact, algorithmic speedup has outpaced hardware gains in the last decades.
Taken together, &lt;a href=&quot;https://scispace.com/pdf/best-subset-selection-via-a-modern-optimization-lens-353jtit2ms.pdf&quot;&gt;this paper&lt;/a&gt;
cites a 450-billion-fold speedup between 1991 and 2015.&lt;/p&gt;
&lt;p&gt;Last but not least: even (5), the archetype of NP-hard problems,
is routinely solved at scale.
Amazon is solving &lt;a href=&quot;https://www.amazon.science/blog/a-billion-smt-queries-a-day&quot;&gt;a billion SMT problems a day&lt;/a&gt;.
SMT is an even harder version of SAT.
The SAT algorithms have gotten so good,
it&#39;s now considered the easy part.&lt;/p&gt;
&lt;p&gt;But what if you run into the worst-case?
You don&#39;t have to wait for the heat-death of the universe.
An HTTP request also doesn&#39;t come back sometimes.
Add a timeout, show an error message, ... you know the drill.&lt;/p&gt;
</description><pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate>
      <dc:creator>Niklas Gruhn</dc:creator>
      <guid>https://gruhn.me/blog/2026-08-13/</guid>
    </item>
    <item>
      <title>Don&#39;t be a meat proxy</title>
      <link>https://gruhn.me/blog/2026-08-03/</link><description>&lt;p&gt;Too often
I ask a question in Slack or
leave feedback under a merge/pull request or
argue with friends in a WhatsApp group and
get back:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Claude said: [giant response verbatim]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please don&#39;t do this.
I mean, I&#39;ve done this.
But I&#39;ve been on the receiving end too many times now.
This is not adding value.
I can talk to Claude myself.
It&#39;s going to be faster and I get to control the context.
I don&#39;t need a meat proxy in between.&lt;/p&gt;
&lt;p&gt;Reading AI output is extra effort.
It&#39;s verbose,
frequently contains all too plausible nonsense,
and is increasingly jargon dense.
I recently got this sentence from Claude:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NATS control-plane events: stream leader election / R3 quorum re-form during pod churn.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jesus. I had to lookup almost every word to make sense of this.&lt;/p&gt;
&lt;p&gt;By all means, prompt AI.
But don&#39;t just relay the output.
Read it, understand it, validate it, and then write a response in your own words
(a decent certificate that you&#39;ve done the prior steps).
Making that effort is value you can add.&lt;/p&gt;
&lt;p&gt;Take code review in particular.
Shipping &lt;em&gt;some&lt;/em&gt; code can be done with close to zero effort now:
Copy/paste the ticket description into Claude Code.
Don&#39;t look at the code or read what Claude has written.
If there&#39;s any feedback from reviewers,
copy/paste that into Claude Code as well.
If necessary, iterate.&lt;/p&gt;
&lt;p&gt;That works.
But who has done the implementation?
The reviewers did, using Claude Code, and you as a meat proxy.&lt;/p&gt;
</description><pubDate>Mon, 03 Aug 2026 00:00:00 +0000</pubDate>
      <dc:creator>Niklas Gruhn</dc:creator>
      <guid>https://gruhn.me/blog/2026-08-03/</guid>
    </item>
    <item>
      <title>What happens at 60% unemployment rate?</title>
      <link>https://gruhn.me/blog/2026-02-22/</link><description>&lt;p&gt;In the US, white-collar work
&lt;a href=&quot;https://www.encyclopedia.com/social-sciences/applied-and-social-sciences-magazines/employment-white-collar&quot;&gt;has grown from 17.6% of total employment in 1900 to ~60% in 2002&lt;/a&gt;.
Presumably, that share has grown even more and other first world countries have seen a similar trajectory.&lt;/p&gt;
&lt;p&gt;Let&#39;s assume AI makes good on its promise to eliminate a significant fraction of these jobs (and doesn&#39;t go rogue).
The remaining 40% of plumbers, chefs, electricians may not be replaceable for the foreseeable future,
but they&#39;re not going to have a better time if a giant chunk of their customer base evaporates.
Also, whatever tasks remain that only humans can complete;
figuring out how to automate those is itself an intellectual task.
At that point, AI can probably get to work on that.&lt;/p&gt;
&lt;p&gt;I have no clue about the timeline.
Let it be 1 year, 10 years, 100 years.
Either way, what happens in the limit?
When humans become fundamentally useless?
What kind of economy is that?
Is that a boom or recession?
Where does the output of all that insane productivity go if consumers have no purchasing power?
Who&#39;s going to buy Teslas and iPhones and shop on Amazon?
Even advertisers and B2B companies are ultimately downstream of consumers.
Why would I buy ad space from Google or Meta if no one can afford the thing I&#39;m trying to sell?
It seems the big tech end game is to erase their own market.&lt;/p&gt;
&lt;p&gt;Everyone is saying we need UBI to prevent a social calamity.
Never mind that, I think without UBI the market economy completely stops making sense.&lt;/p&gt;
&lt;p&gt;I&#39;m not particularly optimistic that UBI is going to happen.
Governments are slow and the financial burden is high.
Even if it happens, recipients have zero negotiating leverage to maintain it long term.&lt;/p&gt;
&lt;p&gt;Are we doomed to descend into autocracy?
I think that also doesn&#39;t make sense.
Autocracies extract value from the many to benefit the few.
But what&#39;s the point if there is no value to extract?
Might as well be the dictator of an ant colony.&lt;/p&gt;
&lt;p&gt;Is everyone just going to starve except a handful of super rich hiding in their
&lt;a href=&quot;https://futuredude.substack.com/p/the-billionaire-bunker-boom-how-the&quot;&gt;luxury bunkers&lt;/a&gt;?
That would be such an absurd outcome,
given that we can feed everyone right now without AGI (kind of).&lt;/p&gt;
&lt;p&gt;So what the hell is going to happen?
I have no answers.
But I don&#39;t even see anyone really asking the question.&lt;/p&gt;
</description><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <dc:creator>Niklas Gruhn</dc:creator>
      <guid>https://gruhn.me/blog/2026-02-22/</guid>
    </item>
  </channel>
</rss>