Feed fetched in 810 ms.
Warning Content type is application/rss+xml; charset=utf-8
, not text/xml
.
Feed is 341,001 characters long.
Warning Feed is missing an ETag.
Feed has a last modified date of Tue, 01 Apr 2025 16:04:59 GMT
.
Warning This feed does not have a stylesheet.
This appears to be an RSS feed.
Feed title: Computer Things
Feed self link matches feed URL.
Feed has 30 items.
First item published on 2025-04-01T16:04:59.000Z
Last item published on 2024-07-02T15:46:13.000Z
Home page URL: https://buttondown.com/hillelwayne
Error Home page does not have a matching feed discovery link in the <head>.1 feed links in <head>
Error Home page does not have a link to the feed in the <body>.
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>Computer Things</title> <link>https://buttondown.com/hillelwayne</link> <description>Hi, I'm Hillel. This is the newsletter version of [my website](https://www.hillelwayne.com). I post all website updates here. I also post weekly content just for the newsletter, on topics like * Formal Methods * Software History and Culture * Fringetech and exotic tooling * The philosophy and theory of software engineering You can see the archive of all public essays [here](https://buttondown.email/hillelwayne/archive/).</description> <atom:link href="https://buttondown.email/hillelwayne/rss" rel="self"/> <language>en-us</language> <lastBuildDate>Tue, 01 Apr 2025 16:04:59 +0000</lastBuildDate> <item> <title>[April Cools] Gaming Games for Non-Gamers</title> <link>https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/</link> <description><p>My <em>April Cools</em> is out! <a href="https://www.hillelwayne.com/post/vidja-games/" target="_blank">Gaming Games for Non-Gamers</a> is a 3,000 word essay on video games worth playing if you've never enjoyed a video game before. <a href="https://www.patreon.com/posts/blog-notes-gamer-125654321?utm_medium=clipboard_copy&amp;utm_source=copyLink&amp;utm_campaign=postshare_creator&amp;utm_content=join_link" target="_blank">Patreon notes here</a>.</p> <p>(April Cools is a project where we write genuine content on non-normal topics. You can see all the other April Cools posted so far <a href="https://www.aprilcools.club/" target="_blank">here</a>. There's still time to submit your own!)</p> <a class="embedded-link" href="https://www.aprilcools.club/"> <div style="width: 100%; background: #fff; border: 1px #ced3d9 solid; border-radius: 5px; margin-top: 1em; overflow: auto; margin-bottom: 1em;"> <div style="float: left; border-bottom: 1px #ced3d9 solid;"> <img class="link-image" src="https://www.aprilcools.club/aprilcoolsclub.png"/> </div> <div style="float: left; color: #393f48; padding-left: 1em; padding-right: 1em;"> <h4 class="link-title" style="margin-bottom: 0em; line-height: 1.25em; margin-top: 1em; font-size: 14px;"> April Cools' Club</h4> </div> </div></a></description> <pubDate>Tue, 01 Apr 2025 16:04:59 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/</guid> </item> <item> <title>Betteridge's Law of Software Engineering Specialness</title> <link>https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/</link> <description><h3>Logic for Programmers v0.8 now out!</h3> <p>The new release has minor changes: new formatting for notes and a better introduction to predicates. I would have rolled it all into v0.9 next month but I like the monthly cadence. <a href="https://leanpub.com/logic/" target="_blank">Get it here!</a></p> <h1>Betteridge's Law of Software Engineering Specialness</h1> <p>In <a href="https://agileotter.blogspot.com/2025/03/there-is-no-automatic-reset-in.html" target="_blank">There is No Automatic Reset in Engineering</a>, Tim Ottinger asks:</p> <blockquote> <p>Do the other people have to live with January 2013 for the rest of their lives? Or is it only engineering that has to deal with every dirty hack since the beginning of the organization?</p> </blockquote> <p><strong>Betteridge's Law of Headlines</strong> says that if a journalism headline ends with a question mark, the answer is probably "no". I propose a similar law relating to software engineering specialness:<sup id="fnref:ottinger"><a class="footnote-ref" href="#fn:ottinger">1</a></sup></p> <blockquote> <p>If someone asks if some aspect of software development is truly unique to just software development, the answer is probably "no".</p> </blockquote> <p>Take the idea that "in software, hacks are forever." My favorite example of this comes from a different profession. The <a href="https://en.wikipedia.org/wiki/Dewey_Decimal_Classification" target="_blank">Dewey Decimal System</a> hierarchically categorizes books by discipline. For example, <em><a href="https://www.librarything.com/work/10143437/t/Covered-Bridges-of-Pennsylvania" target="_blank">Covered Bridges of Pennsylvania</a></em> has Dewey number <code>624.37</code>. <code>6--</code> is the technology discipline, <code>62-</code> is engineering, <code>624</code> is civil engineering, and <code>624.3</code> is "special types of bridges". I have no idea what the last <code>0.07</code> means, but you get the picture.</p> <p>Now if you look at the <a href="https://www.librarything.com/mds/6" target="_blank">6-- "technology" breakdown</a>, you'll see that there's no "software" subdiscipline. This is because when Dewey preallocated the whole technology block in 1876. New topics were instead to be added to the <code>00-</code> "general-knowledge" catch-all. Eventually <code>005</code> was assigned to "software development", meaning <em>The C Programming Language</em> lives at <code>005.133</code>. </p> <p>Incidentally, another late addition to the general knowledge block is <code>001.9</code>: "controversial knowledge". </p> <p>And that's why my hometown library shelved the C++ books right next to <em>The Mothman Prophecies</em>.</p> <p>How's <em>that</em> for technical debt?</p> <p>If anything, fixing hacks in software is significantly <em>easier</em> than in other fields. This came up when I was <a href="https://www.hillelwayne.com/post/we-are-not-special/" target="_blank">interviewing classic engineers</a>. Kludges happened all the time, but "refactoring" them out is <em>expensive</em>. Need to house a machine that's just two inches taller than the room? Guess what, you're cutting a hole in the ceiling.</p> <p>(Even if we restrict the question to other departments in a <em>software company</em>, we can find kludges that are horrible to undo. I once worked for a company which landed an early contract by adding a bespoke support agreement for that one customer. That plagued them for years afterward.)</p> <p>That's not to say that there aren't things that are different about software vs other fields!<sup id="fnref:example"><a class="footnote-ref" href="#fn:example">2</a></sup> But I think that <em>most</em> of the time, when we say "software development is the only profession that deals with XYZ", it's only because we're ignorant of how those other professions work.</p> <hr/> <p>Short newsletter because I'm way behind on writing my <a href="https://www.aprilcools.club/" target="_blank">April Cools</a>. If you're interested in April Cools, you should try it out! I make it <em>way</em> harder on myself than it actually needs to be— everybody else who participates finds it pretty chill.</p> <div class="footnote"> <hr/> <ol> <li id="fn:ottinger"> <p>Ottinger caveats it with "engineering, software or otherwise", so I think he knows that other branches of <em>engineering</em>, at least, have kludges. <a class="footnote-backref" href="#fnref:ottinger" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:example"> <p>The "software is different" idea that I'm most sympathetic to is that in software, the tools we use and the products we create are made from the same material. That's unusual at least in classic engineering. Then again, plenty of machinists have made their own lathes and mills! <a class="footnote-backref" href="#fnref:example" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 26 Mar 2025 18:48:39 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/</guid> </item> <item> <title>Verification-First Development</title> <link>https://buttondown.com/hillelwayne/archive/verification-first-development/</link> <description><p>A while back I argued on the Blue Site<sup id="fnref:li"><a class="footnote-ref" href="#fn:li">1</a></sup> that "test-first development" (TFD) was different than "test-driven development" (TDD). The former is "write tests before you write code", the latter is a paradigm, culture, and collection of norms that's based on TFD. More broadly, TFD is a special case of <strong>Verification-First Development</strong> and TDD is not.</p> <blockquote> <p>VFD: before writing code, put in place some means of verifying that the code is correct, or at least have an idea of what you'll do.</p> </blockquote> <p>"Verifying" could mean writing tests, or figuring out how to encode invariants in types, or <a href="https://blog.regehr.org/archives/1091" target="_blank">adding contracts</a>, or <a href="https://learntla.com/" target="_blank">making a formal model</a>, or writing a separate script that checks the output of the program. Just have <em>something</em> appropriate in place that you can run as you go building the code. Ideally, we'd have verification in place for every interesting property, but that's rarely possible in practice. </p> <p>Oftentimes we can't make the verification until the code is partially complete. In that case it still helps to figure out the verification we'll write later. The point is to have a <em>plan</em> and follow it promptly.</p> <p>I'm using "code" as a standin for anything we programmers make, not just software programs. When using constraint solvers, I try to find representative problems I know the answers to. When writing formal specifications, I figure out the system's properties before the design that satisfies those properties. There's probably equivalents in security and other topics, too.</p> <h3>The Benefits of VFD</h3> <ol> <li>Doing verification before coding makes it less likely we'll skip verification entirely. It's the professional equivalent of "No TV until you do your homework."</li> <li>It's easier to make sure a verifier works properly if we start by running it on code we know doesn't pass it. Bebugging working code takes more discipline.</li> <li>We can run checks earlier in the development process. It's better to realize that our code is broken five minutes after we broke it rather than two hours after.</li> </ol> <p>That's it, those are the benefits of verification-first development. Those are also <em>big</em> benefits for relatively little investment. Specializations of VFD like test-first development can have more benefits, but also more drawbacks.</p> <h3>The drawbacks of VFD</h3> <ol> <li>It slows us down. I know lots of people say that "no actually it makes you go faster in the long run," but that's the <em>long</em> run. Sometimes we do marathons, sometimes we sprint.</li> <li>Verification gets in the way of exploratory coding, where we don't know what exactly we want or how exactly to do something.</li> <li>Any specific form of verification exerts a pressure on our code to make it easier to verify with that method. For example, if we're mostly verifying via type invariants, we need to figure out how to express those things in our language's type system, which may not be suited for the specific invariants we need.<sup id="fnref:sphinx"><a class="footnote-ref" href="#fn:sphinx">2</a></sup></li> </ol> <h2>Whether "pressure" is a real drawback is incredibly controversial</h2> <p>If I had to summarize what makes "test-driven development" different from VFD:<sup id="fnref:tdd"><a class="footnote-ref" href="#fn:tdd">3</a></sup></p> <ol> <li>The form of verification should specifically be tests, and unit tests at that</li> <li>Testing pressure is invariably good. "Making your code easier to unit test" is the same as "making your code better".</li> </ol> <p>This is something all of the various "drivens"— TDD, Type Driven Development, Design by Contract— share in common, this idea that the purpose of the paradigm is to exert pressure. Lots of TDD experts claim that "having a good test suite" is only the secondary benefit of TDD and the real benefit is how it improves code quality.<sup id="fnref:docs"><a class="footnote-ref" href="#fn:docs">4</a></sup></p> <p>Whether they're right or not is not something I want to argue: I've seen these approaches all improve my code structure, but also sometimes worsen it. Regardless, I consider pressure a drawback to VFD in general, though, for a somewhat idiosyncratic reason. If it <em>weren't</em> for pressure, VFD would be wholly independent of the code itself. It would <em>just</em> be about verification, and our decisions would exclusively be about how we want to verify. But the design pressure means that our means of verification affects the system we're checking. What if these conflict in some way?</p> <h3>VFD is a technique, not a paradigm</h3> <p>One of the main differences between "techniques" and "paradigms" is that paradigms don't play well with each other. If you tried to do both "proper" Test-Driven Development and "proper" Cleanroom, your head would explode. Whereas VFD being a "technique" means it works well with other techniques and even with many full paradigms.</p> <p>It also doesn't take a whole lot of practice to start using. It does take practice, both in thinking of verifications and in using the particular verification method involved, to <em>use well</em>, but we can use it poorly and still benefit.</p> <div class="footnote"> <hr/> <ol> <li id="fn:li"> <p>LinkedIn, what did you think I meant? <a class="footnote-backref" href="#fnref:li" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:sphinx"> <p>This bit me in the butt when making my own <a href="https://www.sphinx-doc.org/en/master/" target="_blank">sphinx</a> extensions. The official guides do things in a highly dynamic way that Mypy can't statically check. I had to do things in a completely different way. Ended up being better though! <a class="footnote-backref" href="#fnref:sphinx" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:tdd"> <p>Someone's going to yell at me that I completely missed the point of TDD, which is XYZ. Well guess what, someone else <em>already</em> yelled at me that only dumb idiot babies think XYZ is important in TDD. Put in whatever you want for XYZ. <a class="footnote-backref" href="#fnref:tdd" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:docs"> <p>Another thing that weirdly all of the paradigms claim: that they lead to better documentation. I can see the argument, I just find it strange that <em>every single one</em> makes this claim! <a class="footnote-backref" href="#fnref:docs" title="Jump back to footnote 4 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 18 Mar 2025 16:22:20 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/verification-first-development/</guid> </item> <item> <title>New Blog Post: "A Perplexing Javascript Parsing Puzzle"</title> <link>https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/</link> <description><p>I know I said we'd be back to normal newsletters this week and in fact had 80% of one already written. </p> <p>Then I unearthed something that was better left buried.</p> <p><a href="http://www.hillelwayne.com/post/javascript-puzzle/" target="_blank">Blog post here</a>, <a href="https://www.patreon.com/posts/blog-notes-124153641" target="_blank">Patreon notes here</a> (Mostly an explanation of how I found this horror in the first place). Next week I'll send what was supposed to be this week's piece.</p> <p>(PS: <a href="https://www.aprilcools.club/" target="_blank">April Cools</a> in three weeks!)</p></description> <pubDate>Wed, 12 Mar 2025 14:49:52 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/</guid> </item> <item> <title>Five Kinds of Nondeterminism</title> <link>https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/</link> <description><p>No newsletter next week, I'm teaching a TLA+ workshop.</p> <p>Speaking of which: I spend a lot of time thinking about formal methods (and TLA+ specifically) because it's where the source of almost all my revenue. But I don't share most of the details because 90% of my readers don't use FM and never will. I think it's more interesting to talk about ideas <em>from</em> FM that would be useful to people outside that field. For example, the idea of "property strength" translates to the <a href="https://buttondown.com/hillelwayne/archive/some-tests-are-stronger-than-others/" target="_blank">idea that some tests are stronger than others</a>. </p> <p>Another possible export is how FM approaches nondeterminism. A <strong>nondeterministic</strong> algorithm is one that, from the same starting conditions, has multiple possible outputs. This is nondeterministic:</p> <div class="codehilite"><pre><span></span><code># Pseudocode def f() { return rand()+1; } </code></pre></div> <p>When specifying systems, I may not <em>encounter</em> nondeterminism more often than in real systems, but I am definitely more aware of its presence. Modeling nondeterminism is a core part of formal specification. I mentally categorize nondeterminism into five buckets. Caveat, this is specifically about nondeterminism from the perspective of <em>system modeling</em>, not computer science as a whole. If I tried to include stuff on NFAs and amb operations this would be twice as long.<sup id="fnref:nondeterminism"><a class="footnote-ref" href="#fn:nondeterminism">1</a></sup></p> <p style="height:16px; margin:0px !important;"></p> <h2>1. True Randomness</h2> <p>Programs that literally make calls to a <code>random</code> function and then use the results. This the simplest type of nondeterminism and one of the most ubiquitous. </p> <p>Most of the time, <code>random</code> isn't <em>truly</em> nondeterministic. Most of the time computer randomness is actually <strong>pseudorandom</strong>, meaning we seed a deterministic algorithm that behaves "randomly-enough" for some use. You could "lift" a nondeterministic random function into a deterministic one by adding a fixed seed to the starting state.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Python</span> <span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span><span class="p">,</span> <span class="n">seed</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="n">seed</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">return</span> <span class="n">random</span><span class="p">()</span> <span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="mf">0.23796462709189137</span> <span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="mf">0.23796462709189137</span> </code></pre></div> <p>Often we don't do this because the <em>point</em> of randomness is to provide nondeterminism! We deliberately <em>abstract out</em> the starting state of the seed from our program, because it's easier to think about it as locally nondeterministic.</p> <p>(There's also "true" randomness, like using <a href="https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html#inpage-nav-3-2" target="_blank">thermal noise</a> as an entropy source, which I think are mainly used for cryptography and seeding PRNGs.)</p> <p>Most formal specification languages don't deal with randomness (though some deal with <a href="https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/" target="_blank">probability more broadly</a>). Instead, we treat it as a nondeterministic choice:</p> <div class="codehilite"><pre><span></span><code># software if rand > 0.001 then return a else crash # specification either return a or crash </code></pre></div> <p>This is because we're looking at worst-case scenarios, so it doesn't matter if <code>crash</code> happens 50% of the time or 0.0001% of the time, it's still possible. </p> <h2>2. Concurrency</h2> <div class="codehilite"><pre><span></span><code># Pseudocode global x = 1, y = 0; def thread1() { x++; x++; x++; } def thread2() { y := x; } </code></pre></div> <p>If <code>thread1()</code> and <code>thread2()</code> run sequentially, then (assuming the sequence is fixed) the final value of <code>y</code> is deterministic. If the two functions are started and run simultaneously, then depending on when <code>thread2</code> executes <code>y</code> can be 1, 2, 3, <em>or</em> 4. Both functions are locally sequential, but running them concurrently leads to global nondeterminism.</p> <p>Concurrency is arguably the most <em>dramatic</em> source of nondeterminism. <a href="https://buttondown.com/hillelwayne/archive/what-makes-concurrency-so-hard/" target="_blank">Small amounts of concurrency lead to huge explosions in the state space</a>. We have words for the specific kinds of nondeterminism caused by concurrency, like "race condition" and "dirty write". Often we think about it as a separate <em>topic</em> from nondeterminism. To some extent it "overshadows" the other kinds: I have a much easier time teaching students about concurrency in models than nondeterminism in models.</p> <p>Many formal specification languages have special syntax/machinery for the concurrent aspects of a system, and generic syntax for other kinds of nondeterminism. In P that's <a href="https://p-org.github.io/P/manual/expressions/#choose" target="_blank">choose</a>. Others don't special-case concurrency, instead representing as it as nondeterministic choices by a global coordinator. This more flexible but also more inconvenient, as you have to implement process-local sequencing code yourself. </p> <h2>3. User Input</h2> <div class="subscribe-form"></div> <p>One of the most famous and influential programming books is <em>The C Programming Language</em> by Kernighan and Ritchie. The first example of a nondeterministic program appears on page 14:</p> <p><img alt="Picture of the book page. Code reproduced below." class="newsletter-image" src="https://assets.buttondown.email/images/94e6ad15-8d09-48df-b885-191318bfd179.jpg?w=960&fit=max"/></p> <p>For the newsletter readers who get text only emails,<sup id="fnref:text-only"><a class="footnote-ref" href="#fn:text-only">2</a></sup> here's the program:</p> <div class="codehilite"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf"><stdio.h></span> <span class="cm">/* copy input to output; 1st version */</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">c</span><span class="p">;</span> <span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getchar</span><span class="p">();</span> <span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">c</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">EOF</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">putchar</span><span class="p">(</span><span class="n">c</span><span class="p">);</span> <span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getchar</span><span class="p">();</span> <span class="w"> </span><span class="p">}</span> <span class="p">}</span> </code></pre></div> <p>Yup, that's nondeterministic. Because the user can enter any string, any call of <code>main()</code> could have any output, meaning the number of possible outcomes is infinity.</p> <p>Okay that seems a little cheap, and I think it's because we tend to think of determinism in terms of how the user <em>experiences</em> the program. Yes, <code>main()</code> has an infinite number of user inputs, but for each input the user will experience only one possible output. It starts to feel more nondeterministic when modeling a long-standing system that's <em>reacting</em> to user input, for example a server that runs a script whenever the user uploads a file. This can be modeled with nondeterminism and concurrency: We have one execution that's the system, and one nondeterministic execution that represents the effects of our user.</p> <p>(One intrusive thought I sometimes have: any "yes/no" dialogue actually has <em>three</em> outcomes: yes, no, or the user getting up and walking away without picking a choice, permanently stalling the execution.)</p> <h2>4. External forces</h2> <p>The more general version of "user input": anything where either 1) some part of the execution outcome depends on retrieving external information, or 2) the external world can change some state outside of your system. I call the distinction between internal and external components of the system <a href="https://www.hillelwayne.com/post/world-vs-machine/" target="_blank">the world and the machine</a>. Simple examples: code that at some point reads an external temperature sensor. Unrelated code running on a system which quits programs if it gets too hot. API requests to a third party vendor. Code processing files but users can delete files before the script gets to them.</p> <p>Like with PRNGs, some of these cases don't <em>have</em> to be nondeterministic; we can argue that "the temperature" should be a virtual input into the function. Like with PRNGs, we treat it as nondeterministic because it's useful to think in that way. Also, what if the temperature changes between starting a function and reading it?</p> <p>External forces are also a source of nondeterminism as <em>uncertainty</em>. Measurements in the real world often comes with errors, so repeating a measurement twice can give two different answers. Sometimes operations fail for no discernable reason, or for a non-programmatic reason (like something physically blocks the sensor).</p> <p>All of these situations can be modeled in the same way as user input: a concurrent execution making nondeterministic choices.</p> <h2>5. Abstraction</h2> <p>This is where nondeterminism in system models and in "real software" differ the most. I said earlier that pseudorandomness is <em>arguably</em> deterministic, but we abstract it into nondeterminism. More generally, <strong>nondeterminism hides implementation details of deterministic processes</strong>.</p> <p>In one consulting project, we had a machine that received a message, parsed a lot of data from the message, went into a complicated workflow, and then entered one of three states. The final state was totally deterministic on the content of the message, but the actual process of determining that final state took tons and tons of code. None of that mattered at the scope we were modeling, so we abstracted it all away: "on receiving message, nondeterministically enter state A, B, or C."</p> <p>Doing this makes the system easier to model. It also makes the model more sensitive to possible errors. What if the workflow is bugged and sends us to the wrong state? That's already covered by the nondeterministic choice! Nondeterministic abstraction gives us the potential to pick the worst-case scenario for our system, so we can prove it's robust even under those conditions.</p> <p>I know I beat the "nondeterminism as abstraction" drum a whole lot but that's because it's the insight from formal methods I personally value the most, that nondeterminism is a powerful tool to <em>simplify reasoning about things</em>. You can see the same approach in how I approach modeling users and external forces: complex realities black-boxed and simplified into nondeterministic forces on the system.</p> <hr/> <p>Anyway, I hope this collection of ideas I got from formal methods are useful to my broader readership. Lemme know if it somehow helps you out!</p> <div class="footnote"> <hr/> <ol> <li id="fn:nondeterminism"> <p>I realized after writing this that I already talked wrote an essay about nondeterminism in formal specification <a href="https://buttondown.com/hillelwayne/archive/nondeterminism-in-formal-specification/" target="_blank">just under a year ago</a>. I hope this one covers enough new ground to be interesting! <a class="footnote-backref" href="#fnref:nondeterminism" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:text-only"> <p>There is a surprising number of you. <a class="footnote-backref" href="#fnref:text-only" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 19 Feb 2025 19:37:57 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/</guid> </item> <item> <title>Are Efficiency and Horizontal Scalability at odds?</title> <link>https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/</link> <description><p>Sorry for missing the newsletter last week! I started writing on Monday as normal, and by Wednesday the piece (about the <a href="https://en.wikipedia.org/wiki/Hierarchy_of_hazard_controls" target="_blank">hierarchy of controls</a> ) was 2000 words and not <em>close</em> to done. So now it'll be a blog post sometime later this month.</p> <p>I also just released a new version of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>! 0.7 adds a bunch of new content (type invariants, modeling access policies, rewrites of the first chapters) but more importantly has new fonts that are more legible than the old ones. <a href="https://leanpub.com/logic/" target="_blank">Go check it out!</a></p> <p>For this week's newsletter I want to brainstorm an idea I've been noodling over for a while. Say we have a computational task, like running a simulation or searching a very large graph, and it's taking too long to complete on a computer. There's generally three things that we can do to make it faster:</p> <ol> <li>Buy a faster computer ("vertical scaling")</li> <li>Modify the software to use the computer's resources better ("efficiency")</li> <li>Modify the software to use multiple computers ("horizontal scaling")</li> </ol> <p>(Splitting single-threaded software across multiple threads/processes is sort of a blend of (2) and (3).)</p> <p>The big benefit of (1) is that we (usually) don't have to make any changes to the software to get a speedup. The downside is that for the past couple of decades computers haven't <em>gotten</em> much faster, except in ways that require recoding (like GPUs and multicore). This means we rely on (2) and (3), and we can do both to a point. I've noticed, though, that horizontal scaling seems to conflict with efficiency. Software optimized to scale well tends to be worse or the <code>N=1</code> case than software optimized to, um, be optimized. </p> <p>Are there reasons to <em>expect</em> this? It seems reasonable that design goals of software are generally in conflict, purely because exclusively optimizing for one property means making decisions that impede other properties. But is there something in the nature of "efficiency" and "horizontal scalability" that make them especially disjoint?</p> <p>This isn't me trying to explain a fully coherent idea, more me trying to figure this all out to myself. Also I'm probably getting some hardware stuff wrong</p> <h3>Amdahl's Law</h3> <p>According to <a href="https://en.wikipedia.org/wiki/Amdahl%27s_law" target="_blank">Amdahl's Law</a>, the maximum speedup by parallelization is constrained by the proportion of the work that can be parallelized. If 80% of algorithm X is parallelizable, the maximum speedup from horizontal scaling is 5x. If algorithm Y is 25% parallelizable, the maximum speedup is only 1.3x. </p> <p>If you need horizontal scalability, you want to use algorithm X, <em>even if Y is naturally 3x faster</em>. But if Y was 4x faster, you'd prefer it to X. Maximal scalability means finding the optimal balance between baseline speed and parallelizability. Maximal efficiency means just optimizing baseline speed. </p> <h3>Coordination Overhead</h3> <p>Distributed algorithms require more coordination. To add a list of numbers in parallel via <a href="https://en.wikipedia.org/wiki/Fork%E2%80%93join_model" target="_blank">fork-join</a>, we'd do something like this:</p> <ol> <li>Split the list into N sublists</li> <li>Fork a new thread/process for sublist</li> <li>Wait for each thread/process to finish</li> <li>Add the sums together.</li> </ol> <p>(1), (2), and (3) all add overhead to the algorithm. At the very least, it's extra lines of code to execute, but it can also mean inter-process communication or network hops. Distribution also means you have fewer natural correctness guarantees, so you need more administrative overhead to avoid race conditions. </p> <p><strong>Real world example:</strong> Historically CPython has a "global interpreter lock" (GIL). In multithreaded code, only one thread could execute Python code at a time (others could execute C code). The <a href="https://docs.python.org/3/howto/free-threading-python.html#single-threaded-performance" target="_blank">newest version</a> supports disabling the GIL, which comes at a 40% overhead for single-threaded programs. Supposedly the difference is because the <a href="https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep659" target="_blank">specializing adaptor</a> optimization isn't thread-safe yet. The Python team is hoping on getting it down to "only" 10%. </p> <p style="height:16px; margin:0px !important;"></p> <h3>Scaling loses shared resources</h3> <p>I'd say that intra-machine scaling (multiple threads/processes) feels qualitatively <em>different</em> than inter-machine scaling. Part of that is that intra-machine scaling is "capped" while inter-machine is not. But there's also a difference in what assumptions you can make about shared resources. Starting from the baseline of single-threaded program:</p> <ol> <li>Threads have a much harder time sharing CPU caches (you have to manually mess with affinities)</li> <li>Processes have a much harder time sharing RAM (I think you have to use <a href="https://en.wikipedia.org/wiki/Memory-mapped_file" target="_blank">mmap</a>?)</li> <li>Machines can't share cache, RAM, or disk, period.</li> </ol> <p>It's a lot easier to solve a problem when the whole thing fits in RAM. But if you split a 50 gb problem across three machines, it doesn't fit in ram by default, even if the machines have 64 gb each. Scaling also means that separate machines can't reuse resources like database connections.</p> <h3>Efficiency comes from limits</h3> <p>I think the two previous points tie together in the idea that maximal efficiency comes from being able to make assumptions about the system. If we know the <em>exact</em> sequence of computations, we can aim to minimize cache misses. If we don't have to worry about thread-safety, <a href="https://www.playingwithpointers.com/blog/refcounting-harder-than-it-sounds.html" target="_blank">tracking references is dramatically simpler</a>. If we have all of the data in a single database, our query planner has more room to work with. At various tiers of scaling these assumptions are no longer guaranteed and we lose the corresponding optimizations.</p> <p>Sometimes these assumptions are implicit and crop up in odd places. Like if you're working at a scale where you need multiple synced databases, you might want to use UUIDs instead of numbers for keys. But then you lose the assumption "recently inserted rows are close together in the index", which I've read <a href="https://www.cybertec-postgresql.com/en/unexpected-downsides-of-uuid-keys-in-postgresql/" target="_blank">can lead to significant slowdowns</a>. </p> <p>This suggests that if you can find a limit somewhere else, you can get both high horizontal scaling and high efficiency. <del>Supposedly the <a href="https://tigerbeetle.com/" target="_blank">TigerBeetle database</a> has both, but that could be because they limit all records to <a href="https://docs.tigerbeetle.com/coding/" target="_blank">accounts and transfers</a>. This means every record fits in <a href="https://tigerbeetle.com/blog/2024-07-23-rediscovering-transaction-processing-from-history-and-first-principles/#transaction-processing-from-first-principles" target="_blank">exactly 128 bytes</a>.</del> [A TigerBeetle engineer reached out to tell me that they do <em>not</em> horizontally scale compute, they distribute across multiple nodes for redundancy. <a href="https://lobste.rs/s/5akiq3/are_efficiency_horizontal_scalability#c_ve8ud5" target="_blank">"You can't make it faster by adding more machines."</a>]</p> <p>Does this mean that "assumptions" could be both "assumptions about the computing environment" and "assumptions about the problem"? In the famous essay <a href="http://www.frankmcsherry.org/graph/scalability/cost/2015/01/15/COST.html" target="_blank">Scalability! But at what COST</a>, Frank McSherry shows that his single-threaded laptop could outperform 128-node "big data systems" on PageRank and graph connectivity (via label propagation). Afterwards, he discusses how a different algorithm solves graph connectivity even faster: </p> <blockquote> <p>[Union find] is more line of code than label propagation, but it is 10x faster and 100x less embarassing. … The union-find algorithm is fundamentally incompatible with the graph computation approaches Giraph, GraphLab, and GraphX put forward (the so-called “think like a vertex” model).</p> </blockquote> <p>The interesting thing to me is that his alternate makes more "assumptions" than what he's comparing to. He can "assume" a fixed goal and optimize the code for that goal. The "big data systems" are trying to be general purpose compute platforms and have to pick a model that supports the widest range of possible problems. </p> <p>A few years back I wrote <a href="https://www.hillelwayne.com/post/cleverness/" target="_blank">clever vs insightful code</a>, I think what I'm trying to say here is that efficiency comes from having insight into your problem and environment.</p> <p>(Last thought to shove in here: to exploit assumptions, you need <em>control</em>. Carefully arranging your data to fit in L1 doesn't matter if your programming language doesn't let you control where things are stored!)</p> <h3>Is there a cultural aspect?</h3> <p>Maybe there's also a cultural element to this conflict. What if the engineers interested in "efficiency" are different from the engineers interested in "horizontal scaling"?</p> <p>At my first job the data scientists set up a <a href="https://en.wikipedia.org/wiki/Apache_Hadoop" target="_blank">Hadoop</a> cluster for their relatively small dataset, only a few dozen gigabytes or so. One of the senior software engineers saw this and said "big data is stupid." To prove it, he took one of their example queries, wrote a script in Go to compute the same thing, and optimized it to run faster on his machine.</p> <p>At the time I was like "yeah, you're right, big data IS stupid!" But I think now that we both missed something obvious: with the "scalable" solution, the data scientists <em>didn't</em> have to write an optimized script for every single query. Optimizing code is hard, adding more machines is easy! </p> <p>The highest-tier of horizontal scaling is usually something large businesses want, and large businesses like problems that can be solved purely with money. Maximizing efficiency requires a lot of knowledge-intensive human labour, so is less appealing as an investment. Then again, I've seen a lot of work on making the scalable systems more efficient, such as evenly balancing heterogeneous workloads. Maybe in the largest systems intra-machine efficiency is just too small-scale a problem. </p> <h3>I'm not sure where this fits in but scaling a volume of tasks conflicts less than scaling individual tasks</h3> <p>If you have 1,000 machines and need to crunch one big graph, you probably want the most scalable algorithm. If you instead have 50,000 small graphs, you probably want the most efficient algorithm, which you then run on all 1,000 machines. When we call a problem <a href="https://en.wikipedia.org/wiki/Embarrassingly_parallel" target="_blank">embarrassingly parallel</a>, we usually mean it's easy to horizontally scale. But it's also one that's easy to make more efficient, because local optimizations don't affect the scaling! </p> <hr/> <p>Okay that's enough brainstorming for one week.</p> <h3>Blog Rec</h3> <p>Whenever I think about optimization as a skill, the first article that comes to mind is <a href="https://matklad.github.io/" target="_blank">Mat Klad's</a> <a href="https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html" target="_blank">Push Ifs Up And Fors Down</a>. I'd never have considered on my own that inlining loops into functions could be such a huge performance win. The blog has a lot of other posts on the nuts-and-bolts of systems languages, optimization, and concurrency.</p></description> <pubDate>Wed, 12 Feb 2025 18:26:20 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/</guid> </item> <item> <title>What hard thing does your tech make easy?</title> <link>https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/</link> <description><p>I occasionally receive emails asking me to look at the writer's new language/library/tool. Sometimes it's in an area I know well, like formal methods. Other times, I'm a complete stranger to the field. Regardless, I'm generally happy to check it out.</p> <p>When starting out, this is the biggest question I'm looking to answer:</p> <blockquote> <p>What does this technology make easy that's normally hard?</p> </blockquote> <p>What justifies me learning and migrating to a <em>new</em> thing as opposed to fighting through my problems with the tools I already know? The new thing has to have some sort of value proposition, which could be something like "better performance" or "more secure". The most universal value and the most direct to show is "takes less time and mental effort to do something". I can't accurately judge two benchmarks, but I can see two demos or code samples and compare which one feels easier to me.</p> <h2>Examples</h2> <h3>Functional programming</h3> <p>What drew me originally to functional programming was higher order functions. </p> <div class="codehilite"><pre><span></span><code># Without HOFs out = [] for x in input { if test(x) { out.append(x) } } # With HOFs filter(test, input) </code></pre></div> <p style="height:16px; margin:0px !important;"></p> <p>We can also compare the easiness of various tasks between examples within the same paradigm. If I know FP via Clojure, what could be appealing about Haskell or F#? For one, null safety is a lot easier when I've got option types.</p> <h3>Array Programming</h3> <p>Array programming languages like APL or J make certain classes of computation easier. For example, finding all of the indices where two arrays <del>differ</del> match. Here it is in Python:</p> <div class="codehilite"><pre><span></span><code><span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="n">y</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="o">>>></span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span> <span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">]</span> <span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span> </code></pre></div> <p>And here it is in J:</p> <div class="codehilite"><pre><span></span><code><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">4</span> <span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">4</span> <span class="w"> </span><span class="nv">I</span><span class="o">.</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">y</span> <span class="mi">7</span><span class="w"> </span><span class="mi">9</span> </code></pre></div> <p>Not every tool is meant for every programmer, because you might not have any of the problems a tool makes easier. What comes up more often for you: filtering a list or finding all the indices where two lists differ? Statistically speaking, functional programming is more useful to you than array programming.</p> <p>But <em>I</em> have this problem enough to justify learning array programming.</p> <h3>LLMs</h3> <p>I think a lot of the appeal of LLMs is they make a lot of specialist tasks easy for nonspecialists. One thing I recently did was convert some rst <a href="https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table" target="_blank">list tables</a> to <a href="https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1" target="_blank">csv tables</a>. Normally I'd have to do write some tricky parsing and serialization code to automatically convert between the two. With LLMs, it's just</p> <blockquote> <p>Convert the following rst list-table into a csv-table: [table]</p> </blockquote> <p>"Easy" can trump "correct" as a value. The LLM might get some translations wrong, but it's so convenient I'd rather manually review all the translations for errors than write specialized script that is correct 100% of the time.</p> <h2>Let's not take this too far</h2> <p>A college friend once claimed that he cracked the secret of human behavior: humans do whatever makes them happiest. "What about the martyr who dies for their beliefs?" "Well, in their last second of life they get REALLY happy."</p> <p>We can do the same here, fitting every value proposition into the frame of "easy". CUDA makes it easier to do matrix multiplication. Rust makes it easier to write low-level code without memory bugs. TLA+ makes it easier to find errors in your design. Monads make it easier to sequence computations in a lazy environment. Making everything about "easy" obscures other reason for adopting new things.</p> <h3>That whole "simple vs easy" thing</h3> <p>Sometimes people think that "simple" is better than "easy", because "simple" is objective and "easy" is subjective. This comes from the famous talk <a href="https://www.infoq.com/presentations/Simple-Made-Easy/" target="_blank">Simple Made Easy</a>. I'm not sure I agree that simple is better <em>or</em> more objective: the speaker claims that polymorphism and typeclasses are "simpler" than conditionals, and I doubt everybody would agree with that.</p> <p>The problem is that "simple" is used to mean both "not complicated" <em>and</em> "not complex". And everybody agrees that "complicated" and "complex" are different, even if they can't agree <em>what</em> the difference is. This idea should probably expanded be expanded into its own newsletter.</p> <p>It's also a lot harder to pitch a technology on being "simpler". Simplicity by itself doesn't make a tool better equipped to solve problems. Simplicity can unlock other benefits, like compositionality or <a href="https://buttondown.com/hillelwayne/archive/the-capability-tractability-tradeoff/" target="_blank">tractability</a>, that provide the actual value. And often that value is in the form of "makes some tasks easier". </p></description> <pubDate>Wed, 29 Jan 2025 18:09:47 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/</guid> </item> <item> <title>The Juggler's Curse</title> <link>https://buttondown.com/hillelwayne/archive/the-jugglers-curse/</link> <description><p>I'm making a more focused effort to juggle this year. Mostly <a href="https://youtu.be/PPhG_90VH5k?si=AxOO65PcX4ZwnxPQ&t=49" target="_blank">boxes</a>, but also classic balls too.<sup id="fnref:boxes"><a class="footnote-ref" href="#fn:boxes">1</a></sup> I've gotten to the point where I can almost consistently do a five-ball cascade, which I <em>thought</em> was the cutoff to being a "good juggler". "Thought" because I now know a "good juggler" is one who can do the five-ball cascade with <em>outside throws</em>. </p> <p>I know this because I can't do the outside five-ball cascade... yet. But it's something I can see myself eventually mastering, unlike the slightly more difficult trick of the five-ball mess, which is impossible for mere mortals like me. </p> <p><em>In theory</em> there is a spectrum of trick difficulties and skill levels. I could place myself on the axis like this:</p> <p><img alt="A crudely-drawn scale with 10 even ticks, I'm between 5 and 6" class="newsletter-image" src="https://assets.buttondown.email/images/8ee51aa1-5dd4-48b8-8110-2cdf9a273612.png?w=960&fit=max"/></p> <p>In practice, there are three tiers:</p> <ol> <li>Toddlers</li> <li>Good jugglers who practice hard</li> <li>Genetic freaks and actual wizards</li> </ol> <p>And the graph always, <em>always</em> looks like this:</p> <p><img alt="The same graph, with the top compressed into "wizards" and bottom into "toddlers". I'm in toddlers." class="newsletter-image" src="https://assets.buttondown.email/images/04c76cec-671e-4560-b64e-498b7652359e.png?w=960&fit=max"/></p> <p>This is the jugglers curse, and it's a three-parter:</p> <ol> <li>The threshold between you and "good" is the next trick you cannot do.</li> <li>Everything below that level is trivial. Once you've gotten a trick down, you can never go back to not knowing it, to appreciating how difficult it was to learn in the first place.<sup id="fnref:expert-blindness"><a class="footnote-ref" href="#fn:expert-blindness">2</a></sup></li> <li>Everything above that level is just "impossible". You don't have the knowledge needed to recognize the different tiers.<sup id="fnref:dk"><a class="footnote-ref" href="#fn:dk">3</a></sup></li> </ol> <p>So as you get better, the stuff that was impossible becomes differentiable, and you can see that some of it <em>is</em> possible. And everything you learned becomes trivial. So you're never a good juggler until you learn "just one more hard trick".</p> <p>The more you know, the more you know you don't know and the less you know you know.</p> <h3>This is supposed to be a software newsletter</h3> <blockquote> <p>A monad is a monoid in the category of endofunctors, what's the problem? <a href="https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html" target="_blank">(src)</a></p> </blockquote> <p>I think this applies to any difficult topic? Most fields don't have the same stark <a href="https://en.wikipedia.org/wiki/Spectral_line" target="_blank">spectral lines</a> as juggling, but there's still tiers of difficulty to techniques, which get compressed the further in either direction they are from your current level.</p> <p>Like, I'm not good at formal methods. I've written two books on it but I've never mastered a dependently-typed language or a theorem prover. Those are equally hard. And I'm not good at modeling concurrent systems because I don't understand the formal definition of bisimulation and haven't implemented a Raft. Those are also equally hard, in fact exactly as hard as mastering a theorem prover.</p> <p>At the same time, the skills I've already developed are easy: properly using refinement is <em>exactly as easy</em> as writing <a href="https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/" target="_blank">a wrapped counter</a>. Then I get surprised when I try to explain strong fairness to someone and they just don't get how □◇(ENABLED〈A〉ᵥ) is <em>obviously</em> different from ◇□(ENABLED 〈A〉ᵥ).</p> <p>Juggler's curse!</p> <p>Now I don't actually know if this is actually how everybody experiences expertise or if it's just my particular personality— I was a juggler long before I was a software developer. Then again, I'd argue that lots of people talk about one consequence of the juggler's curse: imposter syndrome. If you constantly think what you know is "trivial" and what you don't know is "impossible", then yeah, you'd start feeling like an imposter at work real quick.</p> <p>I wonder if part of the cause is that a lot of skills you have to learn are invisible. One of my favorite blog posts ever is <a href="https://www.benkuhn.net/blub/" target="_blank">In Defense of Blub Studies</a>, which argues that software expertise comes through understanding "boring" topics like "what all of the error messages mean" and "how to use a debugger well". Blub is a critical part of expertise and takes a lot of hard work to learn, but it <em>feels</em> like trivia. So looking back on a skill I mastered, I might think it was "easy" because I'm not including all of the blub that I had to learn, too.</p> <p>The takeaway, of course, is that the outside five-ball cascade <em>is</em> objectively the cutoff between good jugglers and toddlers.</p> <div class="footnote"> <hr/> <ol> <li id="fn:boxes"> <p>Rant time: I <em>love</em> cigar box juggling. It's fun, it's creative, it's totally unlike any other kind of juggling. And it's so niche I straight up cannot find anybody in Chicago to practice with. I once went to a juggling convention and was the only person with a cigar box set there. <a class="footnote-backref" href="#fnref:boxes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:expert-blindness"> <p>This particular part of the juggler's curse is also called <a href="https://en.wikipedia.org/wiki/Curse_of_knowledge" target="_blank">the curse of knowledge</a> or "expert blindness". <a class="footnote-backref" href="#fnref:expert-blindness" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:dk"> <p>This isn't Dunning-Kruger, because DK says that people think they are <em>better</em> than they actually are, and also <a href="https://www.mcgill.ca/oss/article/critical-thinking/dunning-kruger-effect-probably-not-real" target="_blank">may not actually be real</a>. <a class="footnote-backref" href="#fnref:dk" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 22 Jan 2025 18:50:40 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/the-jugglers-curse/</guid> </item> <item> <title>What are the Rosettas of formal specification?</title> <link>https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/</link> <description><p>First of all, I just released version 0.6 of <em>Logic for Programmers</em>! You can get it <a href="https://leanpub.com/logic/" target="_blank">here</a>. Release notes in the footnote.<sup id="fnref:release-notes"><a class="footnote-ref" href="#fn:release-notes">1</a></sup></p> <p>I've been thinking about my next project after the book's done. One idea is to do a survey of new formal specification languages. There's been a lot of new ones in the past few years (P, Quint, etc), plus some old ones I haven't critically examined (SPIN, mcrl2). I'm thinking of a brief overview of each, what's interesting about it, and some examples of the corresponding models.</p> <p>For this I'd want a set of "Rosetta" examples. <a href="https://rosettacode.org/wiki/Rosetta_Code" target="_blank">Rosetta Code</a> is a collection of programming tasks done in different languages. For example, <a href="https://rosettacode.org/wiki/99_bottles_of_beer" target="_blank">"99 bottles of beer on the wall"</a> in over 300 languages. If I wanted to make a Rosetta Code for specifications of concurrent systems, what examples would I use? </p> <h3>What makes a good Rosetta examples?</h3> <p>A good Rosetta example would be simple enough to understand and implement but also showcase the differences between the languages. </p> <p>A good example of a Rosetta example is <a href="https://github.com/hwayne/lets-prove-leftpad" target="_blank">leftpad for code verification</a>. Proving leftpad correct is short in whatever verification language you use. But the proofs themselves are different enough that you can compare what it's like to use code contracts vs with dependent types, etc. </p> <p>A <em>bad</em> Rosetta example is "hello world". While it's good for showing how to run a language, it doesn't clearly differentiate languages. Haskell's "hello world" is almost identical to BASIC's "hello world".</p> <p>Rosetta examples don't have to be flashy, but I <em>want</em> mine to be flashy. Formal specification is niche enough that regardless of my medium, most of my audience hasn't use it and may be skeptical. I always have to be selling. This biases me away from using things like dining philosophers or two-phase commit.</p> <p>So with that in mind, three ideas:</p> <h3>1. Wrapped Counter</h3> <p>A counter that starts at 1 and counts to N, after which it wraps around to 1 again.</p> <h4>Why it's good</h4> <p>This is a good introductory formal specification: it's a minimal possible stateful system without concurrency or nondeterminism. You can use it to talk about the basic structure of a spec, how a verifier works, etc. It also a good way of introducing "boring" semantics, like conditionals and arithmetic, and checking if the language does anything unusual with them. Alloy, for example, defaults to 4-bit signed integers, so you run into problems if you set N too high.<sup id="fnref:alloy"><a class="footnote-ref" href="#fn:alloy">2</a></sup></p> <p>At the same time, wrapped counters are a common building block of complex systems. Lots of things can be represented this way: <code>N=1</code> is a flag or blinker, <code>N=3</code> is a traffic light, <code>N=24</code> is a clock, etc.</p> <p>The next example is better for showing basic <a href="https://www.hillelwayne.com/post/safety-and-liveness/" target="_blank">safety and liveness properties</a>, but this will do in a pinch. </p> <h3>2. Threads</h3> <p>A counter starts at 0. N threads each, simultaneously try to update the counter. They do this nonatomically: first they read the value of the counter and store that in a thread-local <code>tmp</code>, then they increment <code>tmp</code>, then they set the counter to <code>tmp</code>. The expected behavior is that the final value of the counter will be N.</p> <h4>Why it's good</h4> <p>The system as described is bugged. If two threads interleave the setlocal commands, one thread update can "clobber" the other and the counter can go backwards. To my surprise, most people <em>do not</em> see this error. So it's a good showcase of how the language actually finds real bugs, and how it can verify fixes.</p> <p>As to actual language topics: the spec covers concurrency and track process-local state. A good spec language should make it possible to adjust N without having to add any new variables. And it "naturally" introduces safety, liveness, and <a href="https://www.hillelwayne.com/post/action-properties/" target="_blank">action</a> properties.</p> <p>Finally, the thread spec is endlessly adaptable. I've used variations of it to teach refinement, resource starvation, fairness, livelocks, and hyperproperties. Tweak it a bit and you get dining philosophers.</p> <h3>3. Bounded buffer</h3> <p>We have a bounded buffer with maximum length <code>X</code>. We have <code>R</code> reader and <code>W</code> writer processes. Before writing, writers first check if the buffer is full. If full, the writer goes to sleep. Otherwise, the writer wakes up <em>a random</em> sleeping process, then pushes an arbitrary value. Readers work the same way, except they pop from the buffer (and go to sleep if the buffer is empty).</p> <p>The only way for a sleeping process to wake up is if another process successfully performs a read or write.</p> <h4>Why it's good</h4> <p>This shows process-local nondeterminism (in choosing which sleeping process to wake up), different behavior for different types of processes, and deadlocks: it's possible for every reader and writer to be asleep at the same time.</p> <p>The beautiful thing about this example: the spec can only deadlock if <code>X < 2*(R+W)</code>. This is the kind of bug you'd struggle to debug in real code. An in fact, people did struggle: even when presented with a minimal code sample and told there was a bug, many <a href="http://wiki.c2.com/?ExtremeProgrammingChallengeFourteen" target="_blank">testing experts couldn't find it</a>. Whereas a formal model of the same code <a href="https://www.hillelwayne.com/post/augmenting-agile/" target="_blank">finds the bug in seconds</a>. </p> <p>If a spec language can model the bounded buffer, then it's good enough for production systems.</p> <p>On top of that, the bug happens regardless of what writers actually put in the buffer, so you can abstract that all away. This example can demonstrate that you can leave implementation details out of a spec and still find critical errors.</p> <h2>Caveat</h2> <p>This is all with a <em>heavy</em> TLA+ bias. I've modeled all of these systems in TLA+ and it works pretty well for them. That is to say, none of these do things TLA+ is <em>bad</em> at: reachability, subtyping, transitive closures, unbound spaces, etc. I imagine that as I cover more specification languages I'll find new Rosettas.</p> <div class="footnote"> <hr/> <ol> <li id="fn:release-notes"> <ul> <li>Exercises are more compact, answers now show name of exercise in title</li> </ul> <ul> <li>"Conditionals" chapter has new section on nested conditionals</li> </ul> <ul> <li>"Crash course" chapter significantly rewritten</li> <li>Starting migrating to use consistently use <code>==</code> for equality and <code>=</code> for definition. Not everything is migrated yet</li> <li>"Beyond Logic" appendix does a <em>slightly</em> better job of covering HOL and constructive logic</li> <li>Addressed various reader feedback</li> <li>Two new exercises</li> </ul> <p><a class="footnote-backref" href="#fnref:release-notes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:alloy"> <p>You can change the int size in a model run, so this is more "surprising footgun and inconvenience" than "fundamental limit of the specification language." Something still good to know! <a class="footnote-backref" href="#fnref:alloy" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 15 Jan 2025 17:34:40 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/</guid> </item> <item> <title>"Logic for Programmers" Project Update</title> <link>https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/</link> <description><p>Happy new year everyone!</p> <p>I released the first <em>Logic for Programmers</em> alpha six months ago. There's since been four new versions since then, with the November release putting us in beta. Between work and holidays I didn't make much progress in December, but there will be a 0.6 release in the next week or two.</p> <p>People have asked me if the book will ever be available in print, and my answer to that is "when it's done". To keep "when it's done" from being "never", I'm committing myself to <strong>have the book finished by July.</strong> That means roughly six more releases between now and the official First Edition. Then I will start looking for a way to get it printed.</p> <h3>The Current State and What Needs to be Done</h3> <p>Right now the book is 26,000 words. For the most part, the structure is set— I don't plan to reorganize the chapters much. But I still need to fix shortcomings identified by the reader feedback. In particular, a few topics need more on real world applications, and the Alloy chapter is pretty weak. There's also a bunch of notes and todos and "fix this"s I need to go over.</p> <p>I also need to rewrite the introduction and predicate logic chapters. Those haven't changed much since 0.1 and I need to go over them <em>very carefully</em>.</p> <p>After that comes copyediting.</p> <h4>Ugh, Copyediting</h4> <p>Copyediting means going through the entire book to make word and sentence sentence level changes to the flow. An example would be changing</p> <table> <thead> <tr> <th>From</th> <th>To</th> </tr> </thead> <tbody> <tr> <td>I said predicates are just “boolean functions”. That isn’t <em>quite</em> true.</td> <td>It's easy to think of predicates as just "boolean" functions, but there is a subtle and important difference.</td> </tr> </tbody> </table> <p>It's a tiny difference but it reads slightly better to me and makes the book slghtly better. Now repeat that for all 3000-odd sentences in the book and I'm done with copyediting!</p> <p>For the first pass, anyway. Copyediting is miserable. </p> <p>Some of the changes I need to make come from reader feedback, but most will come from going through it line-by-line with a copyeditor. Someone's kindly offered to do some of this for free, but I want to find a professional too. If you know anybody, let me know.</p> <h4>Formatting</h4> <p>The book, if I'm being honest, looks ugly. I'm using the default sphinx/latex combination for layout and typesetting. My thinking is it's not worth making the book pretty until it's worth reading. But I also want the book, when it's eventually printed, to look <em>nice</em>. At the very least it shouldn't have "self-published" vibes. </p> <p>I've found someone who's been giving me excellent advice on layout and I'm slowly mastering the LaTeX formatting arcana. It's gonna take a few iterations to get things right.</p> <h4>Front cover</h4> <p>Currently the front cover is this:</p> <p><img alt="Front cover" class="newsletter-image" src="https://assets.buttondown.email/images/b42ee3de-9d8a-4729-809e-a8739741f0cf.png?w=960&fit=max"/></p> <p>It works but gives "programmer spent ten minutes in Inkscape" vibes. I have a vision in my head for what would be nicer. A few people have recommended using Fiverr. So far the results haven't been that good, </p> <h4>Fixing Epub</h4> <p><em>Ugh</em></p> <p>I thought making an epub version would be kinder for phone reading, but it's such a painful format to develop for. Did you know that epub backlinks work totally different on kindle vs other ereaders? Did you know the only way to test if you got em working right is to load them up in a virtual kindle? The feedback loops are miserable. So I've been treating epub as a second-class citizen for now and only fixing the <em>worst</em> errors (like math not rendering properly), but that'll have to change as the book finalizes.</p> <h3>What comes next?</h3> <p>After 1.0, I get my book an ISBN and figure out how to make print copies. The margin on print is <em>way</em> lower than ebooks, especially if it's on-demand: the net royalties for <a href="https://kdp.amazon.com/en_US/help/topic/G201834330" target="_blank">Amazon direct publishing</a> would be 7 dollars on a 20-dollar book (as opposed to Leanpub's 16 dollars). Would having a print version double the sales? I hope so! Either way, a lot of people have been asking about print version so I want to make that possible.</p> <p>(I also want to figure out how to give people who already have the ebook a discount on print, but I don't know if that's feasible.)</p> <p>Then, I dunno, maybe make a talk or a workshop I can pitch to conferences. Once I have that I think I can call <em>LfP</em> complete... at least until the second edition.</p> <hr/> <p>Anyway none of that is actually technical so here's a quick fun thing. I spent a good chunk of my break reading the <a href="https://www.mcrl2.org/web/index.html" target="_blank">mCRL2 book</a>. mCRL2 defines an "algebra" for "communicating processes". As a very broad explanation, that's defining what it means to "add" and "multiply" two processes. What's interesting is that according to their definition, the algebra follows the distributive law, <em>but only if you multiply on the right</em>. eg</p> <div class="codehilite"><pre><span></span><code>// VALID (a+b)*c = a*c + b*c // INVALID a*(b+c) = a*b + a*c </code></pre></div> <p>This is the first time I've ever seen this in practice! Juries still out on the rest of the language.</p> <hr/> <h3>Videos and Stuff</h3> <ul> <li>My <em>DDD Europe</em> talk is now out! <a href="https://www.youtube.com/watch?v=uRmNSuYBUOU" target="_blank">What We Know We Don't Know</a> is about empirical software engineering in general, and software engineering research on Domain Driven Design in particular.</li> <li>I was interviewed in the last video on <a href="https://www.youtube.com/watch?v=yXxmSI9SlwM" target="_blank">Craft vs Cruft</a>'s "Year of Formal Methods". Check it out!</li> </ul></description> <pubDate>Tue, 07 Jan 2025 18:49:40 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/</guid> </item> <item> <title>Formally modeling dreidel, the sequel</title> <link>https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/</link> <description><p>Channukah's next week and that means my favorite pastime, complaining about how <a href="https://en.wikipedia.org/wiki/Dreidel#" target="_blank">Dreidel</a> is a bad game. Last year I formally modeled it in <a href="https://www.prismmodelchecker.org/" target="_blank">PRISM</a> to prove the game's not fun. But because I limited the model to only a small case, I couldn't prove the game was <em>truly</em> bad. </p> <p>It's time to finish the job.</p> <p><img alt="A flaming dreidel, from https://pixelsmerch.com/featured/flaming-dreidel-ilan-rosen.html" class="newsletter-image" src="https://assets.buttondown.email/images/61233445-69a7-4fd4-a024-ee0dca0281c1.jpg?w=960&fit=max"/></p> <h2>The Story so far</h2> <p>You can read the last year's newsletter <a href="https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/" target="_blank">here</a> but here are the high-level notes.</p> <h3>The Game of Dreidel</h3> <ol> <li>Every player starts with N pieces (usually chocolate coins). This is usually 10-15 pieces per player.</li> <li>At the beginning of the game, and whenever the pot is empty, every play antes one coin into the pot.</li> <li> <p>Turns consist of spinning the dreidel. Outcomes are:</p> <ul> <li>נ (Nun): nothing happens.</li> <li>ה (He): player takes half the pot, rounded up.</li> <li>ג (Gimmel): player takes the whole pot, everybody antes.</li> <li>ש (Shin): player adds one of their coins to the pot.</li> </ul> </li> <li> <p>If a player ever has zero coins, they are eliminated. Play continues until only one player remains.</p> </li> </ol> <p>If you don't have a dreidel, you can instead use a four-sided die, but for the authentic experience you should wait eight seconds before looking at your roll.</p> <h3>PRISM</h3> <p><a href="https://www.prismmodelchecker.org/" target="_blank">PRISM</a> is a probabilistic modeling language, meaning you can encode a system with random chances of doing things and it can answer questions like "on average, how many spins does it take before one player loses" (64, for 4 players/10 coins) and "what's the more likely to knock the first player out, shin or ante" (ante is 2.4x more likely). You can see last year's model <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism" target="_blank">here</a>.</p> <p>The problem with PRISM is that it is absurdly inexpressive: it's a thin abstraction for writing giant <a href="https://en.wikipedia.org/wiki/Stochastic_matrix" target="_blank">stochastic matrices</a> and lacks basic affordances like lists or functions. I had to hardcode every possible roll for every player. This meant last year's model had two limits. First, it only handles four players, and I would have to write a new model for three or five players. Second, I made the game end as soon as one player <em>lost</em>:</p> <div class="codehilite"><pre><span></span><code>formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0); </code></pre></div> <p>To fix both of these things, I thought I'd have to treat PRISM as a compilation target, writing a program that took a player count and output the corresponding model. But then December got super busy and I ran out of time to write a program. Instead, I stuck with four hardcoded players and extended the old model to run until victory.</p> <h2>The new model</h2> <p>These are all changes to <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism" target="_blank">last year's model</a>.</p> <p>First, instead of running until one player is out of money, we run until three players are out of money.</p> <div class="codehilite"><pre><span></span><code><span class="gd">- formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0);</span> <span class="gi">+ formula done = </span> <span class="gi">+ ((p1=0) & (p2=0) & (p3=0)) |</span> <span class="gi">+ ((p1=0) & (p2=0) & (p4=0)) |</span> <span class="gi">+ ((p1=0) & (p3=0) & (p4=0)) |</span> <span class="gi">+ ((p2=0) & (p3=0) & (p4=0));</span> </code></pre></div> <p>Next, we change the ante formula. Instead of adding four coins to the pot and subtracting a coin from each player, we add one coin for each player left. <code>min(p1, 1)</code> is 1 if player 1 is still in the game, and 0 otherwise. </p> <div class="codehilite"><pre><span></span><code><span class="gi">+ formula ante_left = min(p1, 1) + min(p2, 1) + min(p3, 1) + min(p4, 1);</span> </code></pre></div> <p>We also have to make sure anteing doesn't end a player with negative money. </p> <div class="codehilite"><pre><span></span><code><span class="gd">- [ante] (pot = 0) & !done -> (pot'=pot+4) & (p1' = p1-1) & (p2' = p2-1) & (p3' = p3-1) & (p4' = p4-1);</span> <span class="gi">+ [ante] (pot = 0) & !done -> (pot'=pot+ante_left) & (p1' = max(p1-1, 0)) & (p2' = max(p2-1, 0)) & (p3' = max(p3-1, 0)) & (p4' = max(p4-1, 0));</span> </code></pre></div> <p>Finally, we have to add logic for a player being "out". Instead of moving to the next player after each turn, we move to the next player still in the game. Also, if someone starts their turn without any coins (f.ex if they just anted their last coin), we just skip their turn. </p> <div class="codehilite"><pre><span></span><code><span class="gi">+ formula p1n = (p2 > 0 ? 2 : p3 > 0 ? 3 : 4);</span> <span class="gi">+ [lost] ((pot != 0) & !done & (turn = 1) & (p1 = 0)) -> (turn' = p1n);</span> <span class="gd">- [spin] ((pot != 0) & !done & (turn = 1)) -></span> <span class="gi">+ [spin] ((pot != 0) & !done & (turn = 1) & (p1 != 0)) -></span> <span class="w"> </span> 0.25: (p1' = p1-1) <span class="w"> </span> & (pot' = min(pot+1, maxval)) <span class="gd">- & (turn' = 2) //shin</span> <span class="gi">+ & (turn' = p1n) //shin</span> </code></pre></div> <p>We make similar changes for all of the other players. You can see the final model <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-02-dreidel-prism" target="_blank">here</a>.</p> <h3>Querying the model</h3> <div class="subscribe-form"></div> <p>So now we have a full game of Dreidel that runs until the player ends. And now, <em>finally</em>, we can see the average number of spins a 4 player game will last.</p> <div class="codehilite"><pre><span></span><code>./prism<span class="w"> </span>dreidel.prism<span class="w"> </span>-const<span class="w"> </span><span class="nv">M</span><span class="o">=</span><span class="m">10</span><span class="w"> </span>-pf<span class="w"> </span><span class="s1">'R=? [F done]'</span><span class="w"> </span> </code></pre></div> <p>In English: each player starts with ten coins. <code>R=?</code> means "expected value of the 'reward'", where 'reward' in this case means number of spins. <code>[F done]</code> weights the reward over all behaviors that reach ("<strong>F</strong>inally") the <code>done</code> state.</p> <div class="codehilite"><pre><span></span><code>Result: 760.5607582661091 Time for model checking: 384.17 seconds. </code></pre></div> <p>So there's the number: 760 spins.<sup id="fnref:ben"><a class="footnote-ref" href="#fn:ben">1</a></sup> At 8 seconds a spin, that's almost two hours for <em>one</em> game.</p> <p>…Jesus, look at that runtime. Six minutes to test one query.</p> <p>PRISM has over a hundred settings that affect model checking, with descriptions like "Pareto curve threshold" and "Use Backwards Pseudo SOR". After looking through them all, I found this perfect combination of configurations that gets the runtime to a more manageable level: </p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism <span class="w"> </span> -const M=10 <span class="w"> </span> -pf 'R=? [F done]' <span class="gi">+ -heuristic speed</span> Result: 760.816255997373 Time for model checking: 13.44 seconds. </code></pre></div> <p>Yes, that's a literal "make it faster" flag.</p> <p>Anyway, that's only the "average" number of spins, weighted across all games. Dreidel has a very long tail. To find that out, we'll use a variation on our query:</p> <div class="codehilite"><pre><span></span><code>const C0; P=? [F <=C0 done] </code></pre></div> <p><code>P=?</code> is the <strong>P</strong>robability something happens. <code>F <=C0 done</code> means we <strong>F</strong>inally reach state <code>done</code> in at most <code>C0</code> steps. By passing in different values of <code>C0</code> we can get a sense of how long a game takes. Since "steps" includes passes and antes, this will overestimate the length of the game. But antes take time too and it should only "pass" on a player once per player, so this should still be a good metric for game length.</p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -const C0=1000:1000:5000 -pf 'const C0; P=? [F <=C0 done]' -heuristic speed C0 Result 1000 0.6259953274918795 2000 0.9098575028069353 3000 0.9783122218576754 4000 0.994782069562932 5000 0.9987446018004976 </code></pre></div> <p>A full 10% of games don't finish in 2000 steps, and 2% pass the 3000 step barrier. At 8 seconds a roll/ante, 3000 steps is over <strong>six hours</strong>.</p> <p>Dreidel is a bad game.</p> <h3>More fun properties</h3> <p>As a sanity check, let's confirm last year's result, that it takes an average of 64ish spins before one player is out. In that model, we just needed to get the total reward. Now we instead want to get the reward until the first state where any of the players have zero coins. <sup id="fnref:co-safe"><a class="footnote-ref" href="#fn:co-safe">2</a></sup></p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -pf 'R=? [F (p1=0 | p2=0 | p3=0 | p4=0)]' -heuristic speed Result: 63.71310116083396 Time for model checking: 2.017 seconds. </code></pre></div> <p>Yep, looks good. With our new model we can also get the average point where two players are out and two players are left. PRISM's lack of abstraction makes expressing the condition directly a little painful, but we can cheat and look for the first state where <code>ante_left <= 2</code>.<sup id="fnref:ante_left"><a class="footnote-ref" href="#fn:ante_left">3</a></sup></p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -pf 'R=? [F (ante_left <= 2)]' -heuristic speed Result: 181.92839196680023 </code></pre></div> <p>It takes twice as long to eliminate the second player as it takes to eliminate the first, and the remaining two players have to go for another 600 spins.</p> <p>Dreidel is a bad game.</p> <h2>The future</h2> <p>There's two things I want to do next with this model. The first is script up something that can generate the PRISM model for me, so I can easily adjust the number of players to 3 or 5. The second is that PRISM has a <a href="https://www.prismmodelchecker.org/manual/PropertySpecification/Filters" target="_blank">filter-query</a> feature I don't understand but I <em>think</em> it could be used for things like "if a player gets 75% of the pot, what's the probability they lose anyway". Otherwise you have to write wonky queries like <code>(P =? [F p1 = 30 & (F p1 = 0)]) / (P =? [F p1 = 0])</code>.<sup id="fnref:lose"><a class="footnote-ref" href="#fn:lose">4</a></sup> But I'm out of time again, so this saga will have to conclude next year.</p> <p>I'm also faced with the terrible revelation that I might be the biggest non-academic user of PRISM.</p> <hr/> <h4><em>Logic for Programmers</em> Khanukah Sale</h4> <p>Still going on! You can get <em>LFP</em> for <a href="https://leanpub.com/logic/c/hannukah-presents" target="_blank">40% off here</a> from now until the end of Xannukkah (Jan 2).<sup id="fnref:joke"><a class="footnote-ref" href="#fn:joke">5</a></sup></p> <h4>I'm in the Raku Advent Calendar!</h4> <p>My piece is called <a href="https://raku-advent.blog/2024/12/11/day-11-counting-up-concurrency/" target="_blank">counting up concurrencies</a>. It's about using Raku to do some combinatorics! Read the rest of the blog too, it's great</p> <div class="footnote"> <hr/> <ol> <li id="fn:ben"> <p>This is different from the <a href="https://www.slate.com/articles/life/holidays/2014/12/rules_of_dreidel_the_hannukah_game_is_way_too_slow_let_s_speed_it_up.html" target="_blank">original anti-Dreidel article</a>: Ben got <em>860</em> spins. That's the average spins if you round <em>down</em> on He, not up. Rounding up on He leads to a shorter game because it means He can empty the pot, which means more antes, and antes are what knocks most players out. <a class="footnote-backref" href="#fnref:ben" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:co-safe"> <p>PRISM calls this <a href="https://www.prismmodelchecker.org/manual/PropertySpecification/Reward-basedProperties" target="_blank">"co-safe LTL reward"</a> and does <em>not</em> explain what that means, nor do most of the papers I found referencing "co-safe LTL". <a href="https://mengguo.github.io/personal_site/papers/pdf/guo2016task.pdf" target="_blank">Eventually</a> I found one that defined it as "any property that only uses X, U, F". <a class="footnote-backref" href="#fnref:co-safe" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:ante_left"> <p>Here's the exact point where I realize I could have defined <code>done</code> as <code>ante_left = 1</code>. Also checking for <code>F (ante_left = 2)</code> gives an expected number of spins as "infinity". I have no idea why. <a class="footnote-backref" href="#fnref:ante_left" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:lose"> <p>10% chances at 4 players / 10 coins. And it takes a minute even <em>with</em> fast mode enabled. <a class="footnote-backref" href="#fnref:lose" title="Jump back to footnote 4 in the text">↩</a></p> </li> <li id="fn:joke"> <p>This joke was funnier before I made the whole newsletter about Chanukahh. <a class="footnote-backref" href="#fnref:joke" title="Jump back to footnote 5 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 18 Dec 2024 16:58:59 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/</guid> </item> <item> <title>Stroustrup's Rule</title> <link>https://buttondown.com/hillelwayne/archive/stroustrups-rule/</link> <description><p>Just finished two weeks of workshops and am <em>exhausted</em>, so this one will be light. </p> <h3>Hanuka Sale</h3> <p><em>Logic for Programmers</em> is on sale until the end of Chanukah! That's Jan 2nd if you're not Jewish. <a href="https://leanpub.com/logic/c/hannukah-presents" target="_blank">Get it for 40% off here</a>.</p> <h1>Stroustrup's Rule</h1> <p>I first encountered <strong>Stroustrup's Rule</strong> on this <a href="https://web.archive.org/web/20240914141601/https:/www.thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/" target="_blank">defunct webpage</a>:</p> <blockquote> <p>One of my favorite insights about syntax design appeared in a <a href="https://learn.microsoft.com/en-us/shows/lang-next-2014/keynote" target="_blank">retrospective on C++</a><sup id="fnref:timing"><a class="footnote-ref" href="#fn:timing">1</a></sup> by Bjarne Stroustrup:</p> <ul> <li>For new features, people insist on <strong>LOUD</strong> explicit syntax. </li> <li>For established features, people want terse notation.</li> </ul> </blockquote> <p>The blogger gives the example of option types in Rust. Originally, the idea of using option types to store errors was new for programmers, so the syntax for passing an error was very explicit:</p> <div class="codehilite"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">File</span><span class="p">::</span><span class="n">open</span><span class="p">(</span><span class="s">"file.txt"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">file</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">file</span><span class="p">,</span> <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">err</span><span class="p">;</span><span class="w"> </span><span class="p">}</span> <span class="p">}</span> </code></pre></div> <p>Once people were more familiar with it, Rust added the <code>try!</code> macro to reduce boilerplate, and finally the <a href="https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md" target="_blank"><code>?</code> operator</a> to streamline error handling further.</p> <p>I see this as a special case of <a href="http://teachtogether.tech/en/index.html#s:models" target="_blank">mental model development</a>: when a feature is new to you, you don't have an internal mental model so need all of the explicit information you can get. Once you're familiar with it, explicit syntax is visual clutter and hinders how quickly you can parse out information.</p> <p>(One example I like: which is more explicit, <code>user_id</code> or <code>user_identifier</code>? Which do experienced programmers prefer?)</p> <p>What's interesting is that it's often the <em>same people</em> on both sides of the spectrum. Beginners need explicit syntax, and as they become experts, they prefer terse syntax. </p> <p>The rule applies to the overall community, too. At the beginning of a language's life, everybody's a beginner. Over time the ratio of experts to beginners changes, and this leads to more focus on "expert-friendly" features, like terser syntax.</p> <p>This can make it harder for beginners to learn the language. There was a lot of drama in Python over the <a href="https://peps.python.org/pep-0572/" target="_blank">"walrus" assignment operator</a>:</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Without walrus</span> <span class="n">val</span> <span class="o">=</span> <span class="nb">dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="c1"># `None` if key absent</span> <span class="k">if</span> <span class="n">val</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="c1"># With walrus</span> <span class="k">if</span> <span class="n">val</span> <span class="o">:=</span> <span class="nb">dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> </code></pre></div> <p>Experts supported it because it made code more elegant, teachers and beginners opposed it because it made the language harder to learn. Explicit syntax vs terse notation.</p> <p>Does this lead to languages bloating over time?</p> <h3>In Teaching</h3> <p>I find that when I teach language workshops I have to actively work against Stroustrup's Rule. The terse notation that easiest for <em>me</em> to read is bad for beginners, who need the explicit syntax that I find grating.</p> <p>One good example is type invariants in TLA+. Say you have a set of workers, and each worker has a counter. Here's two ways to say that every worker's counter is a non-negative integer:</p> <div class="codehilite"><pre><span></span><code>\* Bad \A w \in Workers: counter[w] >= 0 \* Good counter \in [Workers -> Nat] </code></pre></div> <p>The first way literally tests that for every worker, <code>counter[w]</code> is non-negative. The second way tests that the <code>counter</code> mapping as a whole is an element of the appropriate "function set"— all functions between workers and natural numbers.</p> <p>The function set approach is terser, more elegant, and preferred by TLA+ experts. But I teach the "bad" way because it makes more sense to beginners.</p> <div class="footnote"> <hr/> <ol> <li id="fn:timing"> <p>Starts minute 23. <a class="footnote-backref" href="#fnref:timing" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 11 Dec 2024 17:32:53 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/stroustrups-rule/</guid> </item> <item> <title>Hyperproperties</title> <link>https://buttondown.com/hillelwayne/archive/hyperproperties/</link> <description><p>I wrote about <a href="https://hillelwayne.com/post/hyperproperties/" target="_blank">hyperproperties on my blog</a> four years ago, but now an intriguing client problem got me thinking about them again.<sup id="fnref:client"><a class="footnote-ref" href="#fn:client">1</a></sup></p> <p>We're using TLA+ to model a system that starts in state A, and under certain complicated conditions <code>P</code>, transitions to state B. They also had a flag <code>f</code> that, when set, used a different complicated condition <code>Q</code> to check the transitions. As a quick <a href="https://www.hillelwayne.com/post/decision-tables/" target="_blank">decision table</a> (from state <code>A</code>):</p> <table> <thead> <tr> <th>f</th> <th>P</th> <th>Q</th> <th>state'</th> </tr> </thead> <tbody> <tr> <td>F</td> <td>F</td> <td>-</td> <td>A</td> </tr> <tr> <td>F</td> <td>T</td> <td>-</td> <td>B</td> </tr> <tr> <td>T</td> <td>F</td> <td>F</td> <td>A</td> </tr> <tr> <td>T</td> <td>F</td> <td>T</td> <td>B</td> </tr> <tr> <td>T</td> <td>T</td> <td>F</td> <td><strong>impossible</strong></td> </tr> <tr> <td>T</td> <td>T</td> <td>T</td> <td>B</td> </tr> </tbody> </table> <p>The interesting bit is the second-to-last row: Q has to be <em>strictly</em> more permissible than P. The client wanted to verify the property that "the system more aggressively transitions when <code>f</code> is set", ie there is no case where the machine transitions <em>only if <code>f</code> is false</em>.</p> <p><a href="https://www.hillelwayne.com/post/safety-and-liveness/" target="_blank">Regular system properties</a> are specified over states in a single sequence of states (behaviors). <strong>Hyperproperties</strong> can hold over <em>sets</em> of sequences of states. Here the hyperproperties are:</p> <blockquote> <ol> <li>For any two states X and Y in separate behaviors, if the only difference in variable-state between X and Y is that <code>X.f = TRUE</code>, then whenever Y transitions to B, so does X.</li> <li>There is at least one such case where X transitions and Y does not.</li> </ol> </blockquote> <p>That's pretty convoluted, which is par for the course with hyperproperties! It makes a little more sense if you have all of the domain knowledge and specifics. </p> <p>The key thing is that makes this a hyperproperty is that you can't <em>just</em> look at individual behaviors to verify it. Imagine if, when <code>f</code> is true, we <em>never</em> transition to state B. Is that a violation of (1)? Not if we never transition when <code>f</code> is false either! To prove a violation, you need to find a behavior where <code>f</code> is false <em>and</em> the state is otherwise the same <em>and</em> we transition to B anyway.</p> <h4>Aside: states in states in states</h4> <p>I dislike how "state" refers to three things:</p> <ol> <li>The high-level "transition state" of a state-machine</li> <li>A single point in time of a system (the "state space")</li> <li>The mutable data inside your system's <a href="https://www.hillelwayne.com/post/world-vs-machine/" target="_blank">machine</a>.</li> </ol> <p>These are all "close" to each other but <em>just</em> different enough to make conversations confusing. Software is pretty bad about reusing colloquial words like this; don't even get me <em>started</em> on the word "design".</p> <h3>There's a reason we don't talk about hyperproperties</h3> <p>Or three reasons. First of all, hyperproperties make up a <em>vanishingly small</em> percentage of the stuff in a system we care about. We only got to "<code>f</code> makes the system more aggressive" after checking at least a dozen other simpler and <em>more important</em> not-hyper properties.</p> <p>Second, <em>most</em> formal specification languages can't express hyperproperties, and the ones that can are all academic research projects. Modeling systems is hard enough without a generalized behavior notation!</p> <p>Third, hyperproperties are astoundingly expensively to check. As an informal estimation, for a state space of size <code>N</code> regular properties are checked across <code>N</code> individual states and 2-behavior hyperproperties (2-props) are checked across <code>N²</code> pairs. So for a small state space of just a million states, the 2-prop needs to be checked across a <em>trillion</em> pairs. </p> <p>These problems don't apply to "hyperproperties" of functions, just systems. Functions have a lot of interesting hyperproperties, there's an easy way to represent them (call the function twice in a test), and quadratic scaling isn't so bad if you're only testing 100 inputs or so. That's why so-called <a href="https://www.hillelwayne.com/post/metamorphic-testing/" target="_blank">metamorphic testing</a> of functions can be useful.</p> <h3>Checking Hyperproperties Anyway</h3> <p>If we <em>do</em> need to check a hyperproperty, there's a few ways we can approach it. </p> <p>The easiest way is to cheat and find a regular prop that implies the hyperproperty. In client's case, we can abstract <code>P</code> and <code>Q</code> into pure functions and then test that there's no input where <code>P</code> is true and <code>Q</code> is false. In TLA+, this would look something like</p> <div class="codehilite"><pre><span></span><code>\* TLA+ QLooserThanP == \A i1 \in InputSet1, i2 \in Set2: \* ... P(i1, i2, …) => Q(i1, i2, …) </code></pre></div> <p>Of course we can't always encapsulate this way, and this can't catch bugs like "we accidentally use <code>P</code> even if <code>f</code> is true". But it gets the job done.</p> <p>Another way is something I talked about in the <a href="https://hillelwayne.com/post/hyperproperties/" target="_blank">original hyperproperty post</a>: lifting specs into hyperspecs. We create a new spec that initializes two copies of our main spec, runs them in parallel, and then compares their behaviors. See the post for an example. Writing a hyperspec keeps us entirely in TLA+ but takes a lot of work and is <em>very</em> expensive to check. Depending on the property we want to check, we can sometimes find simple optimizations.</p> <p>The last way is something <a href="https://hillelwayne.com/post/graphing-tla/" target="_blank">I explored last year</a>: dump the state graph to disk and treat the hyperproperty as a graph property. In this case, the graph property would be something like </p> <blockquote> <p>Find all graph edges representing an A → B transition. Take all the source nodes of each where <code>f = false</code>. For each such source node, find the corresponding node that's identical except for <code>f = true</code>. That node should be the source of an A → B edge.</p> </blockquote> <p>Upside is you don't have to make any changes to the original spec. Downside is you have to use another programming language for analysis. Also, <a href="https://hillelwayne.com/post/graph-types/" target="_blank">analyzing graphs is terrible</a>. But I think this overall the most robust approach to handling hyperproperties, to be used when "cheating" fails.</p> <hr/> <p>What fascinates me most about this is the four-year gap between "I learned and wrote about hyperproperties" and "I have to deal with hyperproperties in my job." This is one reason learning for the sake of learning can have a lot of long-term benefits.</p> <hr/> <h3>Blog Rec</h3> <p>This week's rec is <a href="https://robertheaton.com/" target="_blank">Robert Heaton</a>. It's a "general interest" software engineering blog with a focus on math, algorithms, and security. Some of my favorites:</p> <ul> <li><a href="https://robertheaton.com/preventing-impossible-game-levels-using-cryptography/" target="_blank">Preventing impossible game levels using cryptography</a> and the whole "Steve Steveington" series</li> <li><a href="https://robertheaton.com/2019/06/24/i-was-7-words-away-from-being-spear-phished/" target="_blank">I was 7 words away from being spear-phished</a> is a great deep dive into one targeted scam</li> <li><a href="https://robertheaton.com/2019/02/24/making-peace-with-simpsons-paradox/" target="_blank">Making peace with Simpson's Paradox</a> is the best explanation of Simpson's Paradox I've ever read.</li> </ul> <p>Other good ones are <a href="https://robertheaton.com/pyskywifi/" target="_blank">PySkyWiFi: completely free, unbelievably stupid wi-fi on long-haul flights</a> and <a href="https://robertheaton.com/interview/" target="_blank">How to pass a coding interview with me</a>. The guy's got <em>breadth</em>.</p> <div class="footnote"> <hr/> <ol> <li id="fn:client"> <p>I do formal methods consulting btw. <a href="https://www.hillelwayne.com/consulting/" target="_blank">Hire me!</a> <a class="footnote-backref" href="#fnref:client" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 19 Nov 2024 19:34:54 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/hyperproperties/</guid> </item> <item> <title>Five Unusual Raku Features</title> <link>https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/</link> <description><h3><a href="https://leanpub.com/logic/" target="_blank"><em>Logic for Programmers</em></a> is now in Beta!</h3> <p><a href="https://leanpub.com/logic/" target="_blank">v0.5 marks the official end of alpha</a>! With the new version, all of the content I wanted to put in the book is now present, and all that's left is copyediting, proofreading, and formatting. Which will probably take as long as it took to actually write the book. You can see the release notes in the footnote.<sup id="fnref:release-notes"><a class="footnote-ref" href="#fn:release-notes">1</a></sup></p> <p>And I've got a snazzy new cover:</p> <p><img alt="The logic for programmers cover, a 40x zoom of a bird feather" class="newsletter-image" src="https://assets.buttondown.email/images/26c75f1e-e60a-4328-96e5-9878d96d3e53.png?w=960&fit=max"/></p> <p>(I don't actually like the cover that much but it <em>looks</em> official enough until I can pay an actual cover designer.)</p> <h1>"Five" Unusual Raku Features</h1> <p>Last year I started learning Raku, and the sheer bizarreness of the language left me describing it as <a href="https://buttondown.com/hillelwayne/archive/raku-a-language-for-gremlins/" target="_blank">a language for gremlins</a>. Now that I've used it in anger for over a year, I have a better way of describing it:</p> <blockquote> <p>Raku is a laboratory for language features.</p> </blockquote> <p>This is why it has <a href="https://docs.raku.org/language/concurrency" target="_blank">five different models of concurrency</a> and eighteen ways of doing anything else, because the point is to <em>see</em> what happens. It also explains why many of the features interact so strangely and why there's all that odd edge-case behavior. Getting 100 experiments polished and playing nicely with each other is much harder than running 100 experiments; we can sort out the polish <em>after</em> we figure out which ideas are good ones.</p> <p>So here are "five" Raku experiments you could imagine seeing in another programming language. If you squint.</p> <h3><a href="https://docs.raku.org/type/Junction" target="_blank">Junctions</a></h3> <p>Junctions are "superpositions of possible values". Applying an operation to a junction instead applies it to every value inside the junction. </p> <div class="codehilite"><pre><span></span><code>> <span class="mi">2</span><span class="o">|</span><span class="mi">10</span> <span class="nb">any</span>(<span class="mi">2</span>, <span class="mi">10</span>) > <span class="mi">2</span><span class="nv">&10</span> + <span class="mi">3</span> <span class="nb">all</span>(<span class="mi">5</span>, <span class="mi">13</span>) >(<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) <span class="nb">all</span>(<span class="nb">one</span>(<span class="mi">11</span>, <span class="mi">21</span>), <span class="nb">one</span>(<span class="mi">12</span>, <span class="mi">22</span>)) </code></pre></div> <p>As you can probably tell from the <code>all</code>s and <code>any</code>s, junctions are a feature meant for representing boolean formula. There's no way to destructure a junction, and the only way to use it is to collapse it to a boolean first.</p> <div class="codehilite"><pre><span></span><code>> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) < <span class="mi">15</span> <span class="nb">all</span>(<span class="nb">one</span>(<span class="nb">True</span>, <span class="nb">False</span>), <span class="nb">one</span>(<span class="nb">True</span>, <span class="nb">False</span>)) <span class="c1"># so coerces junctions to booleans</span> > <span class="nb">so</span> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) < <span class="mi">15</span> <span class="nb">True</span> > <span class="nb">so</span> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) > <span class="mi">0</span> <span class="nb">False</span> > <span class="mi">16</span> %% (<span class="mi">3</span><span class="nv">&5</span>) ?? <span class="s">"fizzbuzz"</span> !! * * </code></pre></div> <p>The real interesting thing for me is how Raku elegantly uses junctions to represent quantifiers. In most languages, you either have the function <code>all(list[T], T -> bool)</code> or the method <code>[T].all(T -> bool)</code>, both of which apply the test to every element of the list. In Raku, though, <code>list.all</code> doesn't take <em>anything</em>, it's just a niladic method that turns the list into a junction. </p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">$x</span> = <span class="s"><1 2 3></span>.<span class="nb">all</span> <span class="nb">all</span>(<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>) > <span class="nb">is-prime</span>(<span class="nv">$x</span>) <span class="nb">all</span>(<span class="nb">False</span>, <span class="nb">True</span>, <span class="nb">True</span>) </code></pre></div> <p>This means we can combine junctions. If Raku didn't already have a <code>unique</code> method, we could build it by saying "are all elements equal to exactly one element?"</p> <div class="codehilite"><pre><span></span><code>> <span class="nb">so</span> {.<span class="nb">all</span> == .<span class="nb">one</span>}(<span class="s"><1 2 3 7></span>) <span class="nb">True</span> > <span class="nb">so</span> {.<span class="nb">all</span> == .<span class="nb">one</span>}(<span class="s"><1 2 3 7 2></span>) <span class="nb">False</span> </code></pre></div> <h3><a href="https://docs.raku.org/type/Whatever" target="_blank">Whatevers</a></h3> <p><code>*</code> is the "whatever" symbol and has a lot of different roles in Raku.<sup id="fnref:analogs"><a class="footnote-ref" href="#fn:analogs">2</a></sup> Some functions and operators have special behavior when passed a <code>*</code>. In a range or sequence, <code>*</code> means "unbound".</p> <div class="codehilite"><pre><span></span><code>> <span class="mi">1</span>..* <span class="mi">1</span><span class="o">..</span><span class="n">Inf</span> > (<span class="mi">2</span>,<span class="mi">4</span>,<span class="mi">8</span>...*)[<span class="mi">17</span>] <span class="mi">262144</span> </code></pre></div> <p>The main built-in use, though, is that expressions with <code>*</code> are lifted into anonymous functions. This is called "whatever-priming" and produces a <code>WhateverCode</code>, which is indistinguishable from other functions except for the type.</p> <div class="codehilite"><pre><span></span><code>> {<span class="nv">$_</span> + <span class="mi">10</span>}(<span class="mi">2</span>) <span class="mi">12</span> > (* + <span class="mi">10</span>)(<span class="mi">2</span>) <span class="mi">12</span> > (^<span class="mi">10</span>).<span class="n">map</span>(* % <span class="mi">2</span>) (<span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span>) </code></pre></div> <p>There's actually a bit of weird behavior here: if <em>two</em> whatevers appear in the expression, they become separate positional variables. <code>(2, 30, 4, 50).map(* + *)</code> returns <code>(32, 54)</code>. This makes it easy to express <a href="https://docs.raku.org/language/operators#infix_..." target="_blank">a tricky Fibonacci definition</a> but otherwise I don't see how it's better than making each <code>*</code> the same value.</p> <p>Regardless, priming is useful because <em>so many</em> Raku methods are overloaded to take functions. You get the last element of a list with <code>l[*-1]</code>. This <em>looks</em> like standard negative-index syntax, but what actually happens is that when <code>[]</code> is passed a function, it passes in list length and looks up the result. So if the list has 10 elements, <code>l[*-1] = l[10-1] = l[9]</code>, aka the last element. Similarly, <code>l.head(2)</code> is the first two elements of a list, <code>l.head(*-2)</code> is all-but-the-last-two.</p> <p>We can pass other functions to <code>[]</code>, which e.g. makes implementing ring buffers easy.</p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">@x</span> = ^<span class="mi">10</span> [<span class="mi">0</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">5</span> <span class="mi">6</span> <span class="mi">7</span> <span class="mi">8</span> <span class="mi">9</span>] > <span class="nv">@x</span>[<span class="mi">95</span> % *]--; <span class="nv">@x</span> [<span class="mi">0</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">4</span> <span class="mi">6</span> <span class="mi">7</span> <span class="mi">8</span> <span class="mi">9</span>] </code></pre></div> <h3><a href="https://docs.raku.org/language/regexes" target="_blank">Regular Expressions</a></h3> <p>There are two basic standards for regexes: POSIX regexes and Perl-compatible regexes (PCRE). POSIX regexes are a terrible mess of backslashes and punctuation. PCRE is backwards compatible with POSIX and is a more terrible mess of backslashes and punctuation. Most languages follow the PCRE standard, but Perl 6 breaks backwards compatibility with an entirely new regex syntax. </p> <p>The most obvious improvement: <a href="https://docs.raku.org/language/regexes#Subrules" target="_blank">composability</a>. In most languages "combine" two regexes by concating their strings together, which is terrible for many, many reasons. Raku has the standard "embed another regex" syntax: <code>/< foo >+/</code> matches one-or-more of the <code>foo</code> regex without <code>foo</code> "leaking" into the top regex. </p> <p>This already does a lot to make regexes more tractable: you can break a complicated regular expression down into simpler and more legible parts. And in fact this is how Raku supports <a href="https://docs.raku.org/language/grammars" target="_blank">parsing grammars</a> as a builtin language feature. I've only used grammars once but it <a href="https://www.hillelwayne.com/post/picat/" target="_blank">was quite helpful</a>.</p> <p>Since we're breaking backwards compatibility anyway, we can now add lots of small QOLs. There's a <a href="https://docs.raku.org/language/regexes#Modified_quantifier:_%,_%%" target="_blank">value separator</a> modifier: <code>\d+ % ','</code> matches <code>1</code> / <code>1,2</code> / <code>1,1,4</code> but not <code>1,</code> or <code>12</code>. <a href="https://docs.raku.org/language/regexes#Lookaround_assertions" target="_blank">Lookaheads</a> and non-capturing groups aren't nonsense glyphs. <code>r1 && r2</code> only matches strings that match <em>both</em> <code>r1</code> and <code>r2</code>. Backtracking can be stopped with <a href="https://docs.raku.org/language/regexes#Preventing_backtracking:_:" target="_blank">:</a>. Whitespace is ignored by default and has to be explicitly enabled in match patterns.</p> <p>There's more stuff Raku does with actually <em>processing</em> regular expressions, but the regex notation is something that might actually appear in another language someday. </p> <p style="height:16px; margin:0px !important;"></p> <h3><a href="https://docs.raku.org/language/operators#Hyper_operators" target="_blank">Hyperoperators</a></h3> <p>This is a small one compared to the other features, but it's also the thing I miss most often in other languages. The most basic form <code>l>>.method</code> is basically equivalent to <code>map</code>, except it also recursively descends into sublists.</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>]>>.<span class="nb">succ</span> [<span class="mi">2</span> [<span class="mi">3</span> <span class="mi">4</span>] <span class="mi">5</span>] </code></pre></div> <p>This is more useful than it looks because any function call <code>f(list, *args)</code> can be rewritten in "method form" <code>list.&f(*args)</code>, so <code>>>.</code> becomes the generalized mapping operator. You can use it with whatevers, too.</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>]>>.&(*+<span class="mi">1</span>) [<span class="mi">2</span> [<span class="mi">3</span> <span class="mi">4</span>] <span class="mi">5</span>] </code></pre></div> <p>Anyway, the more generalized <em>binary</em> hyperoperator <code>l1 << op >> l2</code><sup id="fnref:spaces"><a class="footnote-ref" href="#fn:spaces">3</a></sup> applies <code>op</code> elementwise to the two lists, looping the shorter list until the longer list is exhausted. <code>>>op>></code> / <code><< op<<</code> are the same except they instead loop until the lhs/rhs list is exhausted. Whew!</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> <span class="mi">22</span> <span class="mi">13</span> <span class="mi">24</span> <span class="mi">15</span>] > [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+<< [10, 20]</span> <span class="s">[11 22]</span> <span class="s">> [1, 2, 3, 4, 5] >></span>+>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> <span class="mi">22</span> <span class="mi">13</span> <span class="mi">24</span> <span class="mi">15</span>] <span class="c1"># Also works with single values</span> > [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> <span class="mi">10</span> [<span class="mi">11</span> <span class="mi">12</span> <span class="mi">13</span> <span class="mi">14</span> <span class="mi">15</span>] <span class="c1"># Does weird things with nested lists too</span> > [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> [<span class="mi">22</span> <span class="mi">23</span>] <span class="mi">14</span> <span class="mi">25</span>] </code></pre></div> <p>Also for some reason the hyperoperators have separate behaviors on two hashes, either applying <code>op</code> to the union/intersection/hash difference. </p> <p>Anyway it's a super weird (meta)operator but it's also quite useful! It's the closest thing I've seen to <a href="https://hillelwayne.com/post/j-notation/" target="_blank">J verbs</a> outside an APL. I like using it to run the same formula on multiple possible inputs at once.</p> <div class="codehilite"><pre><span></span><code>(<span class="mi">20</span> * <span class="mi">10</span> <span class="s"><<-></span>> (<span class="mi">21</span>, <span class="mi">24</span>)) <span class="s"><<*></span>> (<span class="mi">10</span>, <span class="mi">100</span>) (<span class="mi">1790</span> <span class="mi">17600</span>) </code></pre></div> <p>Incidentally, it's called the hyperoperator because it evaluates all of the operations in parallel. Explicit loops can be parallelized by prefixing them with <a href="https://docs.raku.org/language/statement-prefixes#hyper,_race" target="_blank"><code>hyper</code></a>.</p> <h3><a href="https://docs.raku.org/type/Pair" target="_blank">Pair Syntax</a></h3> <p>I've talked about pairs a little in <a href="https://buttondown.com/hillelwayne/archive/unusual-basis-types-in-programming-languages/" target="_blank">this newsletter</a>, but the gist is that Raku hashes are composed of a set of pairs <code>key => value</code>. The pair is the basis type, the hash is the collection of pairs. There's also a <em>ton</em> of syntactic sugar for concisely specifying pairs via "colon syntax":</p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">$x</span> = <span class="mi">3</span>; :<span class="nv">$x</span> <span class="nb">x</span> => <span class="mi">3</span> > :<span class="n">a</span><span class="s"><$x></span> <span class="n">a</span> => <span class="s">"$x"</span> > :<span class="n">a</span>(<span class="nv">$x</span>) <span class="n">a</span> => <span class="mi">3</span> > :<span class="mi">3</span><span class="n">a</span> <span class="n">a</span> => <span class="mi">3</span> </code></pre></div> <p>The most important sugars are <code>:key</code> and <code>:!key</code>, which map to <code>key => True</code> and <code>key => False</code>. This is a really elegant way to add flags to a methods! Take the definition of <a href="https://docs.raku.org/type/Str#method_match" target="_blank">match</a>:</p> <div class="codehilite"><pre><span></span><code><span class="k">method</span> <span class="nb">match</span>(<span class="nv">$pat</span>, :<span class="n">continue</span>(:<span class="nv">$c</span>), :<span class="n">pos</span>(:<span class="nv">$p</span>), :<span class="n">global</span>(:<span class="nv">$g</span>), :<span class="n">overlap</span>(:<span class="nv">$ov</span>), :<span class="n">exhaustive</span>(:<span class="nv">$ex</span>), :<span class="n">st</span>(:<span class="nv">$nd</span>), :<span class="n">rd</span>(:<span class="nv">$th</span>), :<span class="nv">$nth</span>, :<span class="nv">$x</span> --> <span class="nb">Match</span>) </code></pre></div> <p>Probably should also mention that in a definition, <code>:f(:$foo)</code> defines the parameter <code>$foo</code> but <a href="https://docs.raku.org/language/signatures#Argument_aliases" target="_blank">also aliases it</a> to <code>:f</code>, so you can set the flag with <code>:f</code> or <code>:foo</code>. Colon-pairs defined in the signature can be passed in anywhere, or even stuck together:</p> <div class="codehilite"><pre><span></span><code>> <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>) 「<span class="n">ab</span>」 > <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>, :<span class="n">g</span>) (「<span class="n">ab</span>」 「<span class="n">ab</span>」) > <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>, :<span class="n">g</span>, :<span class="n">ov</span>) (「<span class="n">ab</span>」 「<span class="n">ba</span>」 「<span class="n">ab</span>」) <span class="c1"># Out of order stuck together</span> > <span class="s">"abab"</span>.<span class="nb">match</span>(:<span class="n">g:ov</span>,<span class="sr"> /../</span>) (「<span class="n">ab</span>」 「<span class="n">ba</span>」 「<span class="n">ab</span>」) </code></pre></div> <p>So that leads to extremely concise method configuration. Definitely beats <code>match(global=True, overlap=True)</code>!</p> <p>And for some reason you can place keyword arguments <em>after</em> the function call:</p> <div class="codehilite"><pre><span></span><code>> <span class="s">"abab"</span>.<span class="nb">match</span>(:<span class="n">g</span>,<span class="sr"> /../</span>):<span class="n">ov:2nd</span> 「<span class="n">ba</span>」 </code></pre></div> <h2>The next-gen lab: Slangs and RakuAST</h2> <p>These are features I have no experience in and <em>certainly</em> are not making their way into other languages, but they really expand the explorable space of new features. <a href="https://raku.land/zef:lizmat/Slangify" target="_blank">Slangs</a> are modifications to the Raku syntax. This can be used for things like <a href="https://raku.land/zef:elcaro/Slang::Otherwise" target="_blank">modifying loop syntax</a>, <a href="https://raku.land/zef:raku-community-modules/Slang::Piersing" target="_blank">changing identifiers</a>, or adding <a href="https://raku.land/zef:raku-community-modules/OO::Actors" target="_blank">actors</a> or <a href="https://raku.land/github:MattOates/BioInfo" target="_blank">DNA sequences</a> to the base language.</p> <p>I <em>barely</em> understand <a href="https://dev.to/lizmat/rakuast-for-early-adopters-576n" target="_blank">RakuAST</a>. I <em>think</em> the idea is that all Raku expressions can be parsed as an AST from inside Raku itself.</p> <div class="codehilite"><pre><span></span><code>> <span class="s">Q/my $x; $x++/</span>.<span class="nb">AST</span> <span class="n">RakuAST::StatementList</span>.<span class="nb">new</span>( <span class="n">RakuAST::Statement::Expression</span>.<span class="nb">new</span>( <span class="n">expression</span> => <span class="n">RakuAST::VarDeclaration::Simple</span>.<span class="nb">new</span>( <span class="nb">sigil</span> => <span class="s">"\$"</span>, <span class="n">desigilname</span> => <span class="n">RakuAST::Name</span>.<span class="n">from-identifier</span>(<span class="s">"x"</span>) ) ), <span class="n">RakuAST::Statement::Expression</span>.<span class="nb">new</span>( <span class="n">expression</span> => <span class="n">RakuAST::ApplyPostfix</span>.<span class="nb">new</span>( <span class="n">operand</span> => <span class="n">RakuAST::Var::Lexical</span>.<span class="nb">new</span>(<span class="s">"\$x"</span>), <span class="nb">postfix</span> => <span class="n">RakuAST::Postfix</span>.<span class="nb">new</span>(<span class="s">"++"</span>) ) ) ) </code></pre></div> <p>This allows for things like writing Raku in different languages:</p> <div class="codehilite"><pre><span></span><code><span class="nb">say</span> <span class="s">Q/my $x; put $x/</span>.<span class="nb">AST</span>.<span class="n">DEPARSE</span>(<span class="s">"NL"</span>) <span class="n">mijn</span> <span class="nv">$x</span>; <span class="n">zeg-het</span> <span class="nv">$x</span> </code></pre></div> <h3>Bonus experiment</h3> <p>Raku comes with a "<a href="https://rakudo.org/star" target="_blank">Rakudo Star</a>" installation, which comes with a set of <a href="https://github.com/rakudo/star/blob/master/etc/modules.txt" target="_blank">blessed third party modules</a> preinstalled. I love this! It's a great compromise between the maintainer burdens of a large standard library and the user burdens of making everybody find the right packages in the ecosystem.</p> <hr/> <h2>Blog Rec</h2> <p>Feel obligated to recommend some Raku blogs! Elizabeth Mattijsen posts <a href="https://dev.to/lizmat" target="_blank">a ton of stuff</a> to dev.to about Raku internals. <a href="https://www.codesections.com/blog/" target="_blank">Codesections</a> has a pretty good blog; he's the person who eventually got me to try out Raku. Finally, the <a href="https://raku-advent.blog/" target="_blank">Raku Advent Calendar</a> is a great dive into advanced Raku techniques. Bad news is it only updates once a year, good news is it's 25 updates that once a year.</p> <div class="footnote"> <hr/> <ol> <li id="fn:release-notes"> <ul> <li>All techniques chapters now have a "Further Reading" section</li> <li>"System modeling" chapter significantly rewritten</li> <li>"Conditionals" chapter expanded, now a real chapter</li> <li>"Logic Programming" chapter now covers datalog, deductive databases</li> <li>"Solvers" chapter has diagram explaining problem</li> <li>Eight new exercises</li> <li>Tentative front cover (will probably change)</li> <li>Fixed some epub issues with math rendering</li> </ul> <p><a class="footnote-backref" href="#fnref:release-notes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:analogs"> <p>Analogues are <a href="https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala/8001065#8001065" target="_blank">Scala's underscore</a>, except unlike Scala it's a value and not syntax, and like Python's <a href="https://docs.python.org/3/library/constants.html#Ellipsis" target="_blank">Ellipses</a>, except it has additional semantics. <a class="footnote-backref" href="#fnref:analogs" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:spaces"> <p>Spaces added so buttondown doesn't think they're tags <a class="footnote-backref" href="#fnref:spaces" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 12 Nov 2024 20:06:55 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/</guid> </item> <item> <title>A list of ternary operators</title> <link>https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/</link> <description><p>Sup nerds, I'm back from SREcon! I had a blast, despite knowing nothing about site reliability engineering and being way over my head in half the talks. I'm trying to catch up on <a href="https://leanpub.com/logic/" target="_blank">The Book</a> and contract work now so I'll do something silly here: ternary operators.</p> <p>Almost all operations on values in programming languages fall into one of three buckets: </p> <ol> <li><strong>Unary operators</strong>, where the operator goes <em>before</em> or <em>after</em> exactly one argument. Examples are <code>x++</code> and <code>-y</code> and <code>!bool</code>. Most languages have a few critical unary operators hardcoded into the grammar. They are almost always symbols, but sometimes are string-identifiers (<code>not</code>).</li> <li><strong>Binary operators</strong>, which are placed <em>between</em> exactly two arguments. Things like <code>+</code> or <code>&&</code> or <code>>=</code>. Languages have a lot more of these than unary operators, because there's more fundamental things we want to do with two values than one value. These can be symbols or identifiers (<code>and</code>).</li> <li>Functions/methods that <em>prefix</em> any number of arguments. <code>func(a, b, c)</code>, <code>obj.method(a, b, c, d)</code>, anything in a lisp. These are how we extend the language, and they almost-exclusively use identifiers and not symbols.<sup id="fnref:lisp"><a class="footnote-ref" href="#fn:lisp">1</a></sup></li> </ol> <p>There's one widespread exception to this categorization: the <strong>ternary operator</strong> <code>bool ? x : y</code>.<sup id="fnref:ternary"><a class="footnote-ref" href="#fn:ternary">2</a></sup> It's an infix operator that takes exactly <em>three</em> arguments and can't be decomposed into two sequential binary operators. <code>bool ? x</code> makes no sense on its own, nor does <code>x : y</code>. </p> <p>Other ternary operators are <em>extremely</em> rare, which is why conditional expressions got to monopolize the name "ternary". But I like how exceptional they are and want to compile some of them. A long long time ago I asked <a href="https://twitter.com/hillelogram/status/1378509881498603527" target="_blank">Twitter</a> for other ternary operators; this is a compilation of some applicable responses plus my own research.</p> <p>(Most of these are a <em>bit</em> of a stretch.)</p> <h3>Stepped Ranges</h3> <p>Many languages have some kind of "stepped range" function:</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Python</span> <span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span> </code></pre></div> <p>There's the "base case" of start and endpoints, and an optional step. Many languages have a binary infix op for the base case, but a few also have a ternary for the optional step:</p> <div class="codehilite"><pre><span></span><code># Frink > map[{|a| a*2}, (1 to 100 step 15) ] [2, 32, 62, 92, 122, 152, 182] # Elixir > IO.puts Enum.join(1..10//2, " ") 1 3 5 7 9 </code></pre></div> <p>This isn't decomposable into two binary ops because you can't assign the range to a value and then step the value later.</p> <h3>Graph ops</h3> <p>In <a href="https://graphviz.org/" target="_blank">Graphviz</a>, a basic edge between two nodes is either the binary <code>node1 -> node2</code> or the ternary <code>node1 -> node2 [edge_props]</code>:</p> <div class="codehilite"><pre><span></span><code><span class="k">digraph</span><span class="w"> </span><span class="nt">G</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">a1</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="nt">a2</span><span class="w"> </span><span class="p">[</span><span class="na">color</span><span class="p">=</span><span class="s2">"green"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div> <p><img alt="Output of the above graphviz" class="newsletter-image" src="https://assets.buttondown.email/images/d1a0f894-59d5-45d3-8702-967e94672371.png?w=960&fit=max"/></p> <p>Graphs seem ternary-friendly because there are three elements involved with any graph connection: the two nodes and the connecting edge. So you also see ternaries in some graph database query languages, with separate places to specify each node and the edge.</p> <div class="codehilite"><pre><span></span><code># GSQL (https://docs.tigergraph.com/gsql-ref/4.1/tutorials/gsql-101/parameterized-gsql-query) SELECT tgt FROM start:s -(Friendship:e)- Person:tgt; # Cypher (https://neo4j.com/docs/cypher-manual/current/introduction/cypher-overview/) MATCH (actor:Actor)-[:ACTED_IN]->(movie:Movie {title: 'The Matrix'}) </code></pre></div> <p>Obligatory plug for my <a href="https://www.hillelwayne.com/post/graph-types/" target="_blank">graph datatype essay</a>.</p> <h3>Metaoperators</h3> <p>Both <a href="https://raku.org/" target="_blank">Raku</a> and <a href="https://www.jsoftware.com/#/README" target="_blank">J</a> have special higher-order functions that apply to binary infixes. Raku calls them <em>metaoperators</em>, while J calls them <em>adverbs</em> and <em>conjugations</em>.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Raku</span> <span class="c1"># `a «op» b` is map, "cycling" shorter list</span> <span class="nb">say</span> <span class="s"><10 20 30></span> «+» <span class="s"><4 5></span> (<span class="mi">14</span> <span class="mi">25</span> <span class="mi">34</span>) <span class="c1"># `a Rop b` is `b op a`</span> <span class="nb">say</span> <span class="mi">2</span> <span class="n">R-</span> <span class="mi">3</span> <span class="mi">1</span> </code></pre></div> <div class="codehilite"><pre><span></span><code><span class="c1">NB. J</span> <span class="c1">NB. x f/ y creates a "table" of x f y</span> <span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">+/</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="mi">20</span> <span class="mi">11</span><span class="w"> </span><span class="mi">21</span> <span class="mi">12</span><span class="w"> </span><span class="mi">22</span> </code></pre></div> <p>The Raku metaoperators are closer to what I'm looking for, since I don't think you can assign the "created operator" directly to a callable variable. J lets you, though!</p> <div class="codehilite"><pre><span></span><code><span class="w"> </span><span class="nv">h</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="o">+/</span> <span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nv">h</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">4</span> <span class="mi">4</span><span class="w"> </span><span class="mi">5</span> <span class="mi">5</span><span class="w"> </span><span class="mi">6</span> </code></pre></div> <p>That said, J has some "decomposable" ternaries that feel <em>spiritually</em> like ternaries, like <a href="https://code.jsoftware.com/wiki/Vocabulary/curlyrt#dyadic" target="_blank">amend</a> and <a href="https://code.jsoftware.com/wiki/Vocabulary/fcap" target="_blank">fold</a>. It also has a special ternary-ish contruct called the "fork".<sup id="fnref:ternaryish"><a class="footnote-ref" href="#fn:ternaryish">3</a></sup> <code>x (f g h) y</code> is parsed as <code>(x f y) g (x h y)</code>:</p> <div class="codehilite"><pre><span></span><code><span class="c1">NB. Max - min</span> <span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="o">>.</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="o"><.</span><span class="p">)</span><span class="w"> </span><span class="mi">2</span> <span class="mi">3</span> <span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="p">(</span><span class="o">>.</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="o"><.</span><span class="p">)</span><span class="w"> </span><span class="mi">5</span> <span class="mi">3</span> </code></pre></div> <p>So at the top level that's just a binary operator, but the binary op is constructed via a ternary op. That's pretty cool IMO.</p> <h3>Assignment Ternaries</h3> <p>Bob Nystrom points out that in many languages, <code>a[b] = c</code> is a ternary operation: it is <em>not</em> the same as <code>x = a[b]; x = c</code>.</p> <p>A weirder case shows up in <a href="https://github.com/betaveros/noulith/" target="_blank">Noulith</a> and Raku (again): update operators. Most languages have the <code>+=</code> <em>binary operator</em>, these two have the <code>f=</code> <em>ternary operator</em>. <code>a f= b</code> is the same as <code>a = f(a, b)</code>.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Raku</span> > <span class="k">my</span> <span class="nv">$x</span> = <span class="mi">2</span>; <span class="nv">$x</span> <span class="nb">max</span>= <span class="mi">3</span>; <span class="nb">say</span> <span class="nv">$x</span> <span class="mi">3</span> </code></pre></div> <p>Arguably this is just syntactic sugar, but I don't think it's decomposable into binary operations.</p> <h3>Custom user ternaries</h3> <p>Tikhon Jelvis pointed out that <a href="https://agda.readthedocs.io/en/v2.7.0.1/language/mixfix-operators.html" target="_blank">Agda</a> lets you define <em>custom</em> mixfix operators, which can be ternary or even tetranary or pentanary. I later found out that <a href="https://docs.racket-lang.org/mixfix/index.html" target="_blank">Racket</a> has this, too. <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html" target="_blank">Objective-C</a> <em>looks</em> like this, too, but feels different somehow. </p> <h3>Near Misses</h3> <p>All of these are arguable, I've just got to draw a line in the sand <em>somewhere</em>.</p> <ul> <li>Regular expression substitutions: <code>s/from/to/flags</code> seems like a ternary, but I'd argue it a datatype constructor, not an expression operator.</li> <li>Comprehensions like <code>[x + 1 | x <- list]</code>: looks like the ternary <code>[expr1 | expr2 <- expr3]</code>, but <code>expr2</code> is only binding a name. Arguably a ternary if you can map <em>and filter</em> in the same expression a la Python or Haskell, but should that be considered sugar for</li> <li>Python's operator chaining (<code>1 < x < 5</code>): syntactic sugar for <code>1 < x and x < 5</code>.</li> <li>Someone suggested <a href="https://stackoverflow.com/questions/7251772/what-exactly-constitutes-swizzling-in-opengl-es-2-0-powervr-sgx-specifically" target="_blank">glsl swizzles</a>, which are very cool but binary operators.</li> </ul> <h2>Why are ternaries so rare?</h2> <p>Ternaries are <em>somewhat</em> more common in math and physics, f.ex in integrals and sums. That's because they were historically done on paper, where you have a 2D canvas, so you can do stuff like this easily:</p> <div class="codehilite"><pre><span></span><code>10 Σ n n=0 </code></pre></div> <p>We express the ternary by putting arguments above and below the operator. All mainstream programming languages are linear, though, so any given symbol has only two sides. Plus functions are more regular and universal than infix operators so you might as well write <code>Sum(n=0, 10, n)</code>. The conditional ternary slips through purely because it's just so darn useful. Though now I'm wondering where it comes from in the first place. Different newsletter, maybe.</p> <p>But I still find ternary operators super interesting, please let me know if you know any I haven't covered!</p> <hr/> <h3>Blog Rec</h3> <p>This week's blog rec is <a href="https://lexi-lambda.github.io/" target="_blank">Alexis King</a>! Generally, Alexis's work spans the theory, practice, and implementation of programming languages, aimed at a popular audience and not an academic one. If you know her for one thing, it's probably <a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate" target="_blank">Parse, don't validate</a>, which is now so mainstream most people haven't read the original post. Another good one is about <a href="https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/" target="_blank">modeling open-world systems with static types</a>. </p> <p>Nowadays she is <em>far</em> more active on <a href="https://langdev.stackexchange.com/users/861/alexis-king" target="_blank">Programming Languages Stack Exchange</a>, where she has blog-length answers on <a href="https://langdev.stackexchange.com/questions/2692/how-should-i-read-type-system-notation/2693#2693" target="_blank">reading type notations</a>, <a href="https://langdev.stackexchange.com/questions/3942/what-are-the-ways-compilers-recognize-complex-patterns/3945#3945" target="_blank">compiler design</a>, and <a href="https://langdev.stackexchange.com/questions/2069/what-is-an-arrow-and-what-powers-would-it-give-as-a-first-class-concept-in-a-pro/2372#2372" target="_blank">why arrows</a>.</p> <div class="footnote"> <hr/> <ol> <li id="fn:lisp"> <p>Unless it's a lisp. <a class="footnote-backref" href="#fnref:lisp" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:ternary"> <p>Or <code>x if bool else y</code>, same thing. <a class="footnote-backref" href="#fnref:ternary" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:ternaryish"> <p>I say "ish" because trains can be arbitrarily long: <code>x (f1 f2 f3 f4 f5) y</code> is something I have <em>no idea</em> <a href="https://code.jsoftware.com/wiki/Vocabulary/fork" target="_blank">how to parse</a>. <a class="footnote-backref" href="#fnref:ternaryish" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 05 Nov 2024 18:40:33 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/</guid> </item> <item> <title>TLA from first principles</title> <link>https://buttondown.com/hillelwayne/archive/tla-from-first-principles/</link> <description><h3>No Newsletter next week</h3> <p>I'll be speaking at <a href="https://www.usenix.org/conference/srecon24emea/presentation/wayne" target="_blank">USENIX SRECon</a>!</p> <h2>TLA from first principles</h2> <p>I'm working on v0.5 of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>. In the process of revising the "System Modeling" chapter, I stumbled on a great way to explain the <strong>T</strong>emporal <strong>L</strong>ogic of <strong>A</strong>ctions that TLA+ is based on. I'm reproducing that bit here with some changes to fit the newsletter format.</p> <p>Note that by this point the reader has already encountered property testing, formal verification, decision tables, and nontemporal specifications, and should already have a lot of practice expressing things as predicates. </p> <hr/> <h3>The intro</h3> <p>We have some bank users, each with an account balance. Bank users can wire money to each other. We have overdraft protection, so wires cannot reduce an account value below zero. </p> <p>For the purposes of introducing the ideas, we'll assume an extremely simple system: two hardcoded variables <code>alice</code> and <code>bob</code>, both start with 10 dollars, and transfers are only from Alice to Bob. Also, the transfer is totally atomic: we check for adequate funds, withdraw, and deposit all in a single moment of time. Later [in the chapter] we'll allow for multiple nonatomic transfers at the same time.</p> <p>First, let's look at a valid <strong>behavior</strong> of the system, or possible way it can evolve.</p> <div class="codehilite"><pre><span></span><code>alice 10 -> 5 -> 3 -> 3 -> ... bob 10 -> 15 -> 17 -> 17 -> ... </code></pre></div> <p>In programming, we'd think of <code>alice</code> and <code>bob</code> as variables that change. How do we represent those variables <em>purely</em> in terms of predicate logic? One way is to instead think of them as <em>arrays</em> of values. <code>alice[0]</code> is the initial state of <code>alice</code>, <code>alice[1]</code> is after the first time step, etc. Time, then, is "just" the set of natural numbers.</p> <div class="codehilite"><pre><span></span><code>Time = {0, 1, 2, 3, ...} alice = [10, 5, 3, 3, ...] bob = [10, 15, 17, 17, ...] </code></pre></div> <p>In comparison to our valid behavior, here are some <em>invalid</em> behaviors:</p> <div class="codehilite"><pre><span></span><code>alice = [10, 3, ...] bob = [10 15, ...] alice = [10, -1, ...] bob = [10 21, ...] </code></pre></div> <p>The first is invalid because Bob received more money than Alice lost. The second is invalid because it violates our proposed invariant, that accounts cannot go negative. Can we write a predicate that is <em>true</em> for valid transitions and <em>false</em> for our two invalid behaviors?</p> <p>Here's one way:</p> <div class="codehilite"><pre><span></span><code>Time = Nat // {0, 1, 2, etc} Transfer(t: Time) = some value in 0..=alice[t]: 1. alice[t+1] = alice[t] - value 2. bob[t+1] = bob[t] + value </code></pre></div> <p>Go through and check that this is true for every <code>t</code> in the valid behavior and false for at least one <code>t</code> in the invalid behavior. Note that the steps where Alice <em>doesn't</em> send a transfer also pass <code>Transfer</code>; we just pick <code>value = 0</code>.</p> <p>I can now write a predicate that perfectly describes a valid behavior:</p> <div class="codehilite"><pre><span></span><code>Spec = 1. alice[0] = 10 2. bob[0] = 10 3. all t in Time: Transfer(t) </code></pre></div> <p>Now allowing "nothing happens" as "Alice sends an empty transfer" is a little bit weird. In the real system, we probably don't want people to constantly be sending each other zero dollars:</p> <div class="codehilite"><pre><span></span><code>Transfer(t: Time) = <span class="gd">- some value in 0..=alice[t]:</span> <span class="gi">+ some value in 1..=alice[t]:</span> <span class="w"> </span> 1. alice[t+1] = alice[t] - value <span class="w"> </span> 2. bob[t+1] = bob[t] + value </code></pre></div> <p>But now there can't be a timestep where nothing happens. And that means <em>no</em> behavior is valid! At every step, Alice <em>must</em> transfer at least one dollar to Bob. Eventually there is some <code>t</code> where <code>alice[t] = 0 && bob[t] = 20</code>. Then Alice can't make a transfer, <code>Transfer(t)</code> is false, and so <code>Spec</code> is false.<sup id="fnref:exercise"><a class="footnote-ref" href="#fn:exercise">1</a></sup></p> <p>So typically when modeling we add a <strong>stutter step</strong>, like this:</p> <div class="codehilite"><pre><span></span><code>Spec = 1. alice[0] = 10 2. bob[0] = 10 3. all t in Time: || Transfer(t) || 1. alice[t+1] = alice[t] 2. bob[t+1] = bob[t] </code></pre></div> <p>(This is also why we can use infinite behaviors to model a finite algorithm. If the algorithm completes at <code>t=21</code>, <code>t=22,23,24...</code> are all stutter steps.)</p> <p>There's enough moving parts here that I'd want to break it into subpredicates.</p> <div class="codehilite"><pre><span></span><code>Init = 1. alice[0] = 10 2. bob[0] = 10 Stutter(t) = 1. alice[t+1] = alice[t] 2. bob[t+1] = bob[t] Next(t) = Transfer(t) // foreshadowing Spec = 1. Init 2. all t in Time: Next(t) || Stutter(t) </code></pre></div> <p>Now finally, how do we represent the property <code>NoOverdrafts</code>? It's an <em>invariant</em> that has to be true at all times. So we do the same thing we did in <code>Spec</code>, write a predicate over all times.</p> <div class="codehilite"><pre><span></span><code>property NoOverdrafts = all t in Time: alice[t] >= 0 && bob[t] >= 0 </code></pre></div> <p>We can even say that <code>Spec => NoOverdrafts</code>, ie if a behavior is valid under <code>Spec</code>, it satisfies <code>NoOverdrafts</code>.</p> <h4>One of the exercises</h4> <p>Modify the <code>Next</code> so that Bob can send Alice transfers, too. Don't try to be too clever, just do this in the most direct way possible.</p> <p>Bonus: can Alice and Bob transfer to each other in the same step?</p> <p><strong>Solution</strong> [in back of book]: We can rename <code>Transfer(t)</code> to <code>TransferAliceToBob(t)</code>, write the converse as a new predicate, and then add it to <code>next</code>. Like this</p> <div class="codehilite"><pre><span></span><code>TransferBobToAlice(t: Time) = some value in 1..=bob[t]: 1. alice[t+1] = alice[t] - value 2. bob[t+1] = bob[t] + value Next(t) = || TransferAliceToBob(t) || TransferBobToAlice(t) </code></pre></div> <p>Now, can Alice and Bob transfer to each other in the same step? No. Let's say they both start with 10 dollars and each try to transfer five dollars to each other. By <code>TransferAliceToBob</code> we have:</p> <div class="codehilite"><pre><span></span><code>1. alice[1] = alice[0] - 5 = 5 2. bob[1] = bob[0] + 5 = 15 </code></pre></div> <p>And by <code>TransferBobToAlice</code>, we have:</p> <div class="codehilite"><pre><span></span><code>1. bob[1] = bob[0] - 5 = 5 2. alice[1] = alice[0] + 5 = 15 </code></pre></div> <p>So now we have <code>alice[1] = 5 && alice[1] = 15</code>, which is always false.</p> <h3>Temporal Logic</h3> <div class="subscribe-form"></div> <p>This is good and all, but in practice, there's two downsides to treating time as a set we can quantify over:</p> <ol> <li>It's cumbersome. We have to write <code>var[t]</code> and <code>var[t+1]</code> all over the place.</li> <li>It's too powerful. We can write expressions like <code>alice[t^2-5] = alice[t] + t</code>.</li> </ol> <p>Problem (2) might seem like a good thing; isn't the whole <em>point</em> of logic to be expressive? But we have a long-term goal in mind: getting a computer to check our formal specification. We need to limit the expressivity of our model so that we can make it checkable. </p> <p>In practice, this will mean making time implicit to our model, instead of explicitly quantifying over it.</p> <p>The first thing we need to do is limit how we can use time. At a given point in time, all we can look at is the <em>current</em> value of a variable (<code>var[t]</code>) and the <em>next</em> value (<code>var[t+1]</code>). No <code>var[t+16]</code> or <code>var[t-1]</code> or anything else complicated.</p> <p>And it turns out we've already seen a mathematical convention for expressing this: <strong>priming</strong>!<sup id="fnref:priming"><a class="footnote-ref" href="#fn:priming">2</a></sup> For a given time <code>t</code>, we can define <code>var</code> to mean <code>var[t]</code> and <code>var'</code> to mean <code>var[t+1]</code>. Then <code>Transfer(t)</code> becomes</p> <div class="codehilite"><pre><span></span><code>Transfer = some value in 1..=alice: 1. alice' = alice 2. bob' = bob </code></pre></div> <p>Next we have the construct <code>all t in Time: P(t)</code> in both <code>Spec</code> and <code>NoOverdrafts</code>. In other words, "P is always true". So we can add <code>always</code> as a new term. Logicians conventionally use □ or <code>[]</code> to mean the same thing.<sup id="fnref:beyond"><a class="footnote-ref" href="#fn:beyond">3</a></sup></p> <div class="codehilite"><pre><span></span><code>property NoOverdrafts = always (alice >= 0 && bob[t] >= 0) // or [](alice >= 0 && bob[t] >= 0) Spec = Init && always (Next || Stutter) </code></pre></div> <p>Now time is <em>almost</em> completely implicit in our spec, with just one exception: <code>Init</code> has <code>alice[0]</code> and <code>bob[0]</code>. We just need one more convention: if a variable is referenced <em>outside</em> of the scope of a temporal operator, it means <code>var[0]</code>. Since <code>Init</code> is outside of the <code>[]</code>, it becomes</p> <div class="codehilite"><pre><span></span><code>Init = 1. alice = 10 2. bob = 10 </code></pre></div> <p>And with that, we've removed <code>Time</code> as an explicit value in our model.</p> <p>The addition of primes and <code>always</code> makes this a <strong>temporal logic</strong>, one that can model how things change over time. And that makes it ideal for modeling software systems.</p> <h3>Modeling with TLA+</h3> <p>One of the most popular specification languages for modeling these kinds of concurrent systems is <strong>TLA+</strong>. TLA+ was invented by the Turing award-winner Leslie Lamport, who also invented a wide variety of concurrency algorithms and LaTeX. Here's our current spec in TLA+:</p> <div class="codehilite"><pre><span></span><code>---- MODULE transfers ---- EXTENDS Integers VARIABLES alice, bob vars == <<alice, bob>> Init == alice = 10 /\ bob = 10 AliceToBob == \E amnt \in 1..alice: alice' = alice - amnt /\ bob' = bob + amnt BobToAlice == \E amnt \in 1..bob: alice' = alice + amnt /\ bob' = bob - amnt Next == AliceToBob \/ BobToAlice Spec == Init /\ [][Next]_vars NoOverdrafts == [](alice >= 0 /\ bob >= 0) ==== </code></pre></div> <p>TLA+ uses ASCII versions of mathematicians notation: <code>/\</code>/<code>\/</code> for <code>&&/||</code>, <code>\A \E</code> for <code>all/some</code>, etc. The only thing that's "unusual" (besides <code>==</code> for definition) is the <code>[][Next]_vars</code> bit. That's TLA+ notation for <code>[](Next || Stutter)</code>: <code>Next</code> or <code>Stutter</code> always happens.</p> <hr/> <p>The rest of the chapter goes on to explain model checking, PlusCal (for modeling nonatomic transactions without needing to explain the exotic TLA+ function syntax), and liveness properties. But this is the intuition behind the "temporal logic of actions": temporal operators are operations on the set of points of time, and we restrict what we can do with those operators to make reasoning about the specification feasible.</p> <p>Honestly I like it enough that I'm thinking of redesigning my TLA+ workshop to start with this explanation. Then again, maybe it only seems good to me because I already know TLA+. Please let me know what you think about it!</p> <p>Anyway, the new version of the chapter will be in v0.5, which should be out mid-November.</p> <hr/> <h3>Blog Rec</h3> <p>This one it's really dear to me: <a href="https://muratbuffalo.blogspot.com/" target="_blank">Metadata</a>, by Murat Demirbas. When I was first trying to learn TLA+ back in 2016, his post <a href="https://muratbuffalo.blogspot.com/2015/01/my-experience-with-using-tla-in.html" target="_blank">on using TLA+ in a distributed systems class</a> was one of, like... <em>three</em> public posts on TLA+. I must have spent hours rereading that post and puzzling out this weird language I stumbled into. Later I emailed Murat with some questions and he was super nice in answering them. Don't think I would have ever grokked TLA+ without him.</p> <p>In addition to TLA+ content, a lot of the blog is also breakdowns of papers he read— like <a href="https://blog.acolyer.org/" target="_blank">the morning paper</a>, except with a focus on distributed systems (and still active). If you're interested in learning more about the science of distributed systems, he has an excellent page on <a href="https://muratbuffalo.blogspot.com/2021/02/foundational-distributed-systems-papers.html" target="_blank">foundational distributed systems papers</a>. But definitely check out his <a href="https://muratbuffalo.blogspot.com/2023/09/metastable-failures-in-wild.html" target="_blank">his deep readings</a>, too!</p> <div class="footnote"> <hr/> <ol> <li id="fn:exercise"> <p>In the book this is presented as an exercise (with the solution in back). The exercise also clarifies that since <code>Time = Nat</code>, all behaviors have an <em>infinite</em> number of steps. <a class="footnote-backref" href="#fnref:exercise" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:priming"> <p>Priming is introduced in the chapter on decision tables, and again in the chapter on database invariants. <code>x'</code> is "the next value of <code>x</code>", so you can use it to express database invariants like "jobs only move from <code>ready</code> to <code>started</code> or <code>aborted</code>." <a class="footnote-backref" href="#fnref:priming" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:beyond"> <p>I'm still vacillating on whether I want a "beyond logic" appendix that covers higher order logic, constructive logic, and modal logic (which is what we're sneakily doing right now!)</p> <p>While I'm here, this explanation of <code>always</code> as <code>all t in Time</code> isn't <em>100%</em> accurate, since it doesn't explain why things like <code>[](P => []Q)</code> or <code><>[]P</code> make sense. But it's accurate in most cases and is a great intuition pump. <a class="footnote-backref" href="#fnref:beyond" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 22 Oct 2024 17:14:21 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/tla-from-first-principles/</guid> </item> <item> <title>Be Suspicious of Success</title> <link>https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/</link> <description><p>From Leslie Lamport's <em>Specifying Systems</em>:</p> <blockquote> <p>You should be suspicious if [the model checker] does not find a violation of a liveness property... you should also be suspicious if [it] finds no errors when checking safety properties. </p> </blockquote> <p>This is specifically in the context of model-checking a formal specification, but it's a widely applicable software principle. It's not enough for a program to work, it has to work for the <em>right reasons</em>. Code working for the wrong reasons is code that's going to break when you least expect it. And since "correct for right reasons" is a much narrower target than "correct for any possible reason", we can't assume our first success is actually our intended success.</p> <p>Hence, BSOS: <strong>Be Suspicious of Success</strong>.</p> <h3>Some useful BSOS practices</h3> <p>The standard way of dealing with BSOS is verification. Tests, static checks, model checking, etc. We get more confident in our code if our verifications succeed. But then we also have to be suspicious of <em>that</em> success, too! How do I know whether my tests are passing because they're properly testing correct code or because they're failing to test incorrect code?</p> <p>This is why test-driven development gurus tell people to write a failing test first. Then at least we know the tests are doing <em>something</em> (even if they still might not be testing what they want).</p> <p>The other limit of verification is that it can't tell us <em>why</em> something succeeds. Mainstream verification methods are good at explaining why things <em>fail</em>— expected vs actual test output, type mismatches, specification error traces. Success isn't as "information-rich" as failure. How do you distinguish a faithful implementation of <a href="https://en.wikipedia.org/wiki/Collatz_conjecture" target="_blank"><code>is_collatz_counterexample</code></a> from <code>return false</code>?</p> <p>A broader technique I follow is <em>make it work, make it break</em>. If code is working for the right reasons, I should be able to predict how to break it. This can be either a change in the runtime (this will livelock if we 10x the number of connections), or a change to the code itself (commenting out <em>this</em> line will cause property X to fail). <sup id="fnref:superproperties"><a class="footnote-ref" href="#fn:superproperties">1</a></sup> If the code still works even after the change, my model of the code is wrong and it was succeeding for the wrong reasons.</p> <h3>Happy and Sad Paths</h3> <div class="subscribe-form"></div> <p>A related topic (possibly subset?) is "happy and sad paths". The happy path of your code is the behavior when everything's going right: correct inputs, preconditions are satisfied, the data sources are present, etc. The sad path is all of the code that handles things going wrong. Retry mechanisms, insufficient user authority, database constraint violation, etc. In most software, the code supporting the sad paths dwarfs the code in the happy path.</p> <p>BSOS says that I can't just show code works in the happy path, I also need to check it works in the sad path. </p> <p>BSOS also says that I have to be suspicious when the sad path works properly, too. </p> <p>Say I add a retry mechanism to my code to handle the failure mode of timeouts. I test the code and it works. Did the retry code actually <em>run</em>? Did it run <em>regardless</em> of the original response? Is it really doing exponential backoff? Will stop after the maximum retry limit? Is the sad path code <em>after</em> the maximum retry limit working properly?</p> <p><a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf" target="_blank">One paper</a> found that 35% of catastrophic distributed system failures were caused by "trivial mistakes in error handlers" (pg 9). These were in mature, battle-hardened programs. Be suspicious of success. Be more suspicious of sad path success.</p> <hr/> <h2>Blog Rec</h2> <p>This week's blog rec is <a href="https://www.redblobgames.com/" target="_blank">Red Blob Games</a>!<sup id="fnref:blogs-vs-articles"><a class="footnote-ref" href="#fn:blogs-vs-articles">2</a></sup> While primarily about computer game programming, the meat of the content is beautiful, interactive guides to general CS algorithms. Some highlights:</p> <ul> <li><a href="https://www.redblobgames.com/pathfinding/a-star/introduction.html" target="_blank">Introduction to the A* Algorithm</a> was really illuminating when I was a baby programmer.</li> <li>I'm sure this <a href="https://www.redblobgames.com/articles/noise/introduction.html" target="_blank">overview of noise functions</a> will be useful to me <em>someday</em>. Maybe for test data generation?</li> <li>If you're also an explainer type he has a lot of great stuff on <a href="https://www.redblobgames.com/making-of/line-drawing/" target="_blank">his process</a> and his <a href="https://www.redblobgames.com/making-of/little-things/" target="_blank">little tricks</a> to make things more understandable.</li> </ul> <p>(I don't think his <a href="https://www.redblobgames.com/blog/posts.xml" target="_blank">rss feed</a> covers new interactive articles, only the <a href="https://www.redblobgames.com/blog/" target="_blank">blog</a> specifically.)</p> <div class="footnote"> <hr/> <ol> <li id="fn:superproperties"> <p><a href="https://www.jameskoppel.com/" target="_blank">Jimmy Koppel</a> once proposed that just as code has properties, code variations have <a href="https://groups.csail.mit.edu/sdg/pubs/2020/demystifying_dependence_published.pdf" target="_blank"><strong>superproperties</strong></a>. For example, "no modification to the codebase causes us to use a greater number of deprecated APIs." <a class="footnote-backref" href="#fnref:superproperties" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:blogs-vs-articles"> <p>Okay, it's more an <em>article</em> site, because there's also a <a href="https://www.redblobgames.com/blog/" target="_blank">Red Blob <em>blog</em></a> (which covers a lot of neat stuff, too). Maybe I should just rename this section to "site rec". <a class="footnote-backref" href="#fnref:blogs-vs-articles" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 16 Oct 2024 15:08:39 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/</guid> </item> <item> <title>How to convince engineers that formal methods is cool</title> <link>https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/</link> <description><p>Sorry there was no newsletter last week! I got COVID. Still got it, which is why this one's also short.</p> <h3>Logic for Programmers v0.4</h3> <p><a href="https://leanpub.com/logic/" target="_blank">Now available</a>! This version adds a chapter on TLA+, significantly expands the constraint solver chapter, and adds a "planner programming" section to the Logic Programming chapter. You can see the full release notes on the <a href="https://leanpub.com/logic/" target="_blank">book page</a>.</p> <h1>How to convince engineers that formal methods is cool</h1> <p>I have an open email for answering questions about formal methods,<sup id="fnref:fs-fv"><a class="footnote-ref" href="#fn:fs-fv">1</a></sup> and one of the most common questions I get is "how do I convince my coworkers that this is worth doing?" usually the context is the reader is really into the idea of FM but their coworkers don't know it exists. The goal of the asker is to both introduce FM and persuade them that FM's useful. </p> <p>In my experience as a consultant and advocate, I've found that there's only two consistently-effective ways to successfully pitch FM:</p> <ol> <li>Use FM to find an <em>existing</em> bug in a work system</li> <li>Show how FM finds a historical bug that's already been fixed.</li> </ol> <h4>Why this works</h4> <p>There's two main objections to FM that we need to address. The first is that FM is too academic and doesn't provide a tangible, practical benefit. The second is that FM is too hard; only PhDs and rocket scientists can economically use it. (Showing use cases from AWS <em>et al</em> aren't broadly persuasive because skeptics don't have any insight into how AWS functions.) Finding an existing bug hits both: it helped the team with a real problem, and it was done by a mere mortal. </p> <div class="subscribe-form"></div> <p>Demonstrating FM on a historical bug isn't <em>as</em> effective: it only shows that formal methods <em>could have</em> helped, not that it actually does help. But people will usually remember the misery of debugging that problem. Bug war stories are popular for a reason!</p> <h3>Making historical bugs persuasive</h3> <p>So "live bug" is a stronger rec, but "historical bug" tends to be easier to show. This is because <em>you know what you're looking for</em>. It's easier to write a high-level spec on a system you already know, and show it finds a bug you already know about.</p> <p>The trick to make it look convincing is to make the spec and bug as "natural" as possible. You can't make it seem like FM only found the bug because you had foreknowledge of what it was— then the whole exercise is too contrived. People will already know you had foreknowledge, of course, and are factoring that into their observations. You want to make the case that the spec you're writing is clear and obvious enough that an "ignorant" person could have written it. That means nothing contrived or suspicious.</p> <p>This is a bit of a fuzzy definition, more a vibe than anything. Ask yourself "does this spec look like something that was tailor-made around this bug, or does it find the bug as a byproduct of being a regular spec?"</p> <p>A good example of a "natural" spec is <a href="https://www.hillelwayne.com/post/augmenting-agile/" target="_blank">the bounded queue problem</a>. It's a straight translation of some Java code with no properties besides deadlock checking. Usually you'll be at a higher level of abstraction, though.</p> <hr/> <h3>Blog rec: <a href="https://www.argmin.net/" target="_blank">arg min</a></h3> <p>This is a new section I want to try for a bit: recommending tech(/-adjacent) blogs that I like. This first one is going to be a bit niche: <a href="https://www.argmin.net/" target="_blank">arg min</a> is writing up lecture notes on "convex optimization". It's a cool look into the theory behind constraint solving. I don't understand most of the math but the prose is pretty approachable. Couple of highlights:</p> <ul> <li><a href="https://www.argmin.net/p/modeling-dystopia" target="_blank">Modeling Dystopia</a> about why constraint solving isn't a mainstream technology.</li> <li><a href="https://www.argmin.net/p/convex-optimization-live-blog" target="_blank">Table of Contents</a> to see all of the posts.</li> </ul> <p>The blogger also talks about some other topics but I haven't read those posts much.</p> <div class="footnote"> <hr/> <ol> <li id="fn:fs-fv"> <p>As always, talking primarily about formal specification of systems (TLA+/Alloy/Spin), not formal verification of code (Dafny/SPARK/Agda). I talk about the differences a bit <a href="https://www.hillelwayne.com/post/why-dont-people-use-formal-methods/" target="_blank">here</a> (but I really need to write a more focused piece). <a class="footnote-backref" href="#fnref:fs-fv" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 08 Oct 2024 16:18:55 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/</guid> </item> <item> <title>Refactoring Invariants</title> <link>https://buttondown.com/hillelwayne/archive/refactoring-invariants/</link> <description><p>(Feeling a little sick so this one will be short.)</p> <p>I'm often asked by clients to review their (usually TLA+) formal specifications. These specs are generally slower and more convoluted than an expert would write. I want to fix them up without changing the overall behavior of the spec or introducing subtle bugs.</p> <p>To do this, I use a rather lovely feature of TLA+. Say I see a 100-line <code>Foo</code> action that I think I can refactor down to 20 lines. I'll first write a refactored version as a separate action <code>NewFoo</code>, then I run the model checker with the property</p> <div class="codehilite"><pre><span></span><code>RefactorProp == [][Foo <=> NewFoo]_vars </code></pre></div> <p>That's an intimidating nest of symbols but all it's saying is that every <code>Foo</code> step must also be a <code>NewFoo</code> step. If the refactor ever does something different from the original action, the model-checker will report the exact behavior and transition it fails for. Conversely, if the model checker passes, I can safely assume they have identical behaviors.</p> <p>This is a <strong>refactoring invariant</strong>:<sup id="fnref:invariant"><a class="footnote-ref" href="#fn:invariant">1</a></sup> the old and new versions of functions have identical behavior. Refactoring invariants are superbly useful in formal specification. Software devs spend enough time refactoring that they'd be useful for coding, too.</p> <p>Alas, refactoring invariants are a little harder to express in code. In TLA+ we're working with bounded state spaces, so the model checker can check the invariant for every possible state. Even a simple program can have an unbounded state space via an infinite number of possible function inputs. </p> <p>(Also formal specifications are "pure" simulations while programs have side effects.)</p> <p>The "normal" way to verify a program refactoring is to start out with a huge suite of <a href="https://buttondown.com/hillelwayne/archive/oracle-testing/" target="_blank">oracle tests</a>. This <em>should</em> catch a bad refactor via failing tests. The downside is that you might not have the test suite in the first place, or not one that covers your particular refactoring. Second, even if the test suite does, it only indirectly tests the invariant. It catches the refactoring error as a consequence of testing other stuff. What if we want to directly test the refactoring invariant?</p> <h3>Two ways of doing this</h3> <p>One: by pulling in formal methods. Ray Myers has a <a href="https://www.youtube.com/watch?v=UdB3XBf219Y" target="_blank">neat video</a> on formally proving a refactoring is correct. That one's in the niche language ACL2, but he's also got one on <a href="https://www.youtube.com/watch?v=_7RXQE-pCMo" target="_blank">refactoring C</a>. You might not even to prove the refactoring correct, you could probably get away with using an <a href="https://github.com/pschanely/CrossHair" target="_blank">SMT solver</a> to find counterexamples.</p> <p>Two: by using property-based testing. Generate random inputs, pass them to both functions, and check that the outputs are identical. Using the python <a href="https://hypothesis.readthedocs.io/en/latest/" target="_blank">Hypothesis</a> library:</p> <div class="codehilite"><pre><span></span><code><span class="kn">from</span> <span class="nn">hypothesis</span> <span class="kn">import</span> <span class="n">given</span> <span class="kn">import</span> <span class="nn">hypothesis.strategies</span> <span class="k">as</span> <span class="nn">st</span> <span class="c1"># from the `gilded rose kata`</span> <span class="k">def</span> <span class="nf">update_quality</span><span class="p">(</span><span class="nb">list</span><span class="p">[</span><span class="n">Item</span><span class="p">]):</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">update_quality_new</span><span class="p">(</span><span class="nb">list</span><span class="p">[</span><span class="n">Item</span><span class="p">]):</span> <span class="o">...</span> <span class="nd">@given</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">lists</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">builds</span><span class="p">(</span><span class="n">Item</span><span class="p">)))</span> <span class="k">def</span> <span class="nf">test_refactoring</span><span class="p">(</span><span class="n">l</span><span class="p">):</span> <span class="k">assert</span> <span class="n">update_quality</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="o">==</span> <span class="n">update_quality_new</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> </code></pre></div> <p>One tricky bit is if the function is part of a long call chain <code>A -> B -> C</code>, and you want to test that refactoring <code>C'</code> doesn't change the behavior of <code>A</code>. You'd have to add a <code>B'</code> that uses <code>C'</code> and then an <code>A'</code> that uses <code>B'</code>. Maybe you could instead create a branch, commit the change the <code>C'</code> in that branch, and then run a <a href="https://www.hillelwayne.com/post/cross-branch-testing/" target="_blank">cross-branch test</a> against each branch's <code>A</code>.</p> <p>Impure functions are harder. The test now makes some side effect twice, which could spuriously break the refactoring invariant. You could instead test the changes are the same, or try to get the functions to effect different entities and then compare the updates of each entity. There's no general solution here though, and there might be No Good Way for a particular effectful refactoring.</p> <h3>Behavior-changing rewrites</h3> <p>We can apply similar ideas for rewrites that change <em>behavior</em>. Say we have an API, and v1 returns a list of user names while v2 returns a <code>{version, userids}</code> dict. Then we can find some transformation of v2 into v1, and run the refactoring invariant on that:</p> <div class="codehilite"><pre><span></span><code><span class="k">def</span> <span class="nf">v2_to_v1</span><span class="p">(</span><span class="n">v2_resp</span><span class="p">):</span> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="o">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">v2_resp</span><span class="p">[</span><span class="s2">"userids"</span><span class="p">]]</span> <span class="nd">@given</span><span class="p">(</span><span class="n">some_query_generator</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_refactoring</span><span class="p">(</span><span class="n">q</span><span class="p">):</span> <span class="k">assert</span> <span class="n">v1</span><span class="p">(</span><span class="n">q</span><span class="p">)</span> <span class="o">==</span> <span class="n">v2_to_v1</span><span class="p">(</span><span class="n">v2</span><span class="p">(</span><span class="n">q</span><span class="p">))</span> </code></pre></div> <p>Fun fact: <code>v2_to_v1</code> is a <a href="https://buttondown.com/hillelwayne/archive/software-isomorphisms/" target="_blank">software homomorphism</a>!</p> <div class="footnote"> <hr/> <ol> <li id="fn:invariant"> <p>Well technically it's an <em>action property</em> since it's on the transitions of states, not the states, but "refactor invariant" gets the idea across better. <a class="footnote-backref" href="#fnref:invariant" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 24 Sep 2024 20:06:10 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/refactoring-invariants/</guid> </item> <item> <title>Goodhart's Law in Software Engineering</title> <link>https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/</link> <description><h3>Blog Hiatus</h3> <p>You might have noticed I haven't been updating my website. I haven't even <em>looked</em> at any of my drafts for the past three months. All that time is instead going into <em>Logic for Programmers</em>. I'll get back to the site when that's done or in 2025, whichever comes first. Newsletter and <a href="https://www.patreon.com/hillelwayne" target="_blank">Patreon</a> will still get regular updates.</p> <p>(As a comparison, the book is now 22k words. That's like 11 blog posts!)</p> <h2>Goodhart's Law in Software Engineering</h2> <p>I recently got into an argument with some people about whether small functions were <em>mostly</em> a good idea or <em>always 100%</em> a good idea, and it reminded me a lot about <a href="https://en.wikipedia.org/wiki/Goodhart%27s_law" target="_blank">Goodhart's Law</a>:</p> <blockquote> <p>When a measure becomes a target, it ceases to be a good measure.</p> </blockquote> <p>The <em>weak</em> version of this is that people have perverse incentives to game the metrics. If your metric is "number of bugs in the bug tracker", people will start spuriously closing bugs just to get the number down. </p> <p>The <em>strong</em> version of the law is that even 100% honest pursuit of a metric, taken far enough, is harmful to your goals, and this is an inescapable consequence of the difference between metrics and values. We have metrics in the first place because what we actually <em>care about</em> is nonquantifiable. There's some <em>thing</em> we want more of, but we have no way of directly measuring that thing. We <em>can</em> measure something that looks like a rough approximation for our goal. But it's <em>not</em> our goal, and if we replace the metric with the goal, we start taking actions that favor the metric over the goal.</p> <p>Say we want more reliable software. How do you measure "reliability"? You can't. But you <em>can</em> measure the number of bugs in the bug tracker, because fewer open bugs roughly means more reliability. <strong>This is not the same thing</strong>. I've seen bugs fixed in ways that made the system <em>less</em> reliable, but not in ways that translated into tracked bugs.</p> <p>I am a firm believer in the strong version of Goodhart's law. Mostly because of this:</p> <p><img alt="A peacock with its feathers out. The peacock is scremming" class="newsletter-image" src="https://assets.buttondown.email/images/2573503d-bc57-49ce-aa26-9d399d801118.jpg?w=960&fit=max"/></p> <p>What does a peahen look for in a mate? A male with maximum fitness. What's a metric that approximates fitness? How nice the plumage is, because nicer plumage = more calories energy to waste on plumage.<sup id="fnref:peacock"><a class="footnote-ref" href="#fn:peacock">1</a></sup> But that only <em>approximates</em> fitness, and over generations the plumage itself becomes the point at the cost of overall bird fitness. Sexual selection is Goodhart's law in action.</p> <div class="subscribe-form"></div> <p>If the blind watchmaker can fall for Goodhart, people can too.</p> <h3>Examples in Engineering</h3> <p>Goodhart's law is a warning for pointy-haired bosses who up with terrible metrics: lines added, feature points done, etc. I'm more interested in how it affects the metrics we set for ourselves that our bosses might never know about.</p> <ul> <li>"Test coverage" is a proxy for how thoroughly we've tested our software. It diverges when we need to test lots of properties of the same lines of code, or when our worst bugs are emergent at the integration level.</li> <li>"Cyclomatic complexity" and "function size" are proxies for code legibility. They diverges when we think about global module legibility, not local function legibility. Then too many functions can obscure the code and data flow.</li> <li>Benchmarks are proxies for performant programs, and diverge when improving benchmarks slows down unbenchmarked operations.</li> <li>Amount of time spent pairing/code reviewing/debugging/whatever proxies "being productive".</li> <li><a href="https://dora.dev/" target="_blank">The DORA report</a> is an interesting case, because it claims four metrics<sup id="fnref:metrics"><a class="footnote-ref" href="#fn:metrics">2</a></sup> are proxies to ineffable goals like "elite performance" and <em>employee satisfaction</em>. It also argues that you should minimize commit size to improve the DORA metrics. A proxy of a proxy of a goal!</li> </ul> <h3>What can we do about this?</h3> <p>No, I do not know how to avoid a law that can hijack the process of evolution.</p> <p>The 2023 DORA report suggests readers should avoid Goodhart's law and "assess a team's strength across a wide range of people, processes, and technical capabilities" (pg 10), which is kind of like saying the fix to production bugs is "don't write bugs". It's a guiding principle but not actionable advice that gets to that principle.</p> <p>They also say "to use a combination of metrics to drive deeper understanding" (ibid), which makes more sense at first. If you have metrics X and Y to approximate goal G, then overoptimizing X <em>might</em> hurt Y, indicating you're getting further from G. In practice I've seen it turn into "we can't improve X because it'll hurt Y and we can't improve Y because it'll hurt X." This <em>could</em> mean we're at the best possible spot for G, but more often it means we're trapped very far from our goal. You could come up with a weighted combination of X and Y, like 0.7X + 0.3Y, but <em>that too</em> is a metric subject to Goodhart. </p> <p>I guess the best I can do is say "use your best engineering judgement"? Evolution is mindless, people aren't. Again, not an actionable or scalable bit of advice, but as I grow older I keep finding "use your best judgement" is all we can do. Knowledge work is ineffable and irreducible.</p> <div class="footnote"> <hr/> <ol> <li id="fn:peacock"> <p>This sent me down a rabbit hole; turns out scientists are still debating what <em>exactly</em> the peacock's tail is used for! Is it sexual selection? Adverse signalling? Something else??? <a class="footnote-backref" href="#fnref:peacock" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:metrics"> <p>How soon commits get to production, deployment frequency, percent of deployments that cause errors in production, and mean time to recovery. <a class="footnote-backref" href="#fnref:metrics" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 17 Sep 2024 16:33:40 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/</guid> </item> <item> <title>Why Not Comments</title> <link>https://buttondown.com/hillelwayne/archive/why-not-comments/</link> <description><h2>Logic For Programmers v0.3</h2> <p><a href="https://leanpub.com/logic/" target="_blank">Now available</a>! It's a light release as I learn more about formatting a nice-looking book. You can see some of the differences between v2 and v3 <a href="https://bsky.app/profile/hillelwayne.com/post/3l3egdqnqj62o" target="_blank">here</a>.</p> <h2>Why Not Comments</h2> <p>Code is written in a structured machine language, comments are written in an expressive human language. The "human language" bit makes comments more expressive and communicative than code. Code has a limited amount of something <em>like</em> human language contained in identifiers. "Comment the why, not the what" means to push as much information as possible into identifiers. <a href="https://buttondown.com/hillelwayne/archive/3866bd6e-22c3-4098-92ef-4d47ef287ed8" target="_blank">Not all "what" can be embedded like this</a>, but a lot can.</p> <p>In recent years I see more people arguing that <em>whys</em> do not belong in comments either, that they can be embedded into <code>LongFunctionNames</code> or the names of test cases. Virtually all "self-documenting" codebases add documentation through the addition of identifiers.<sup id="fnref:exception"><a class="footnote-ref" href="#fn:exception">1</a></sup></p> <p>So what's something in the range of human expression that <em>cannot</em> be represented with more code?</p> <p>Negative information, drawing attention to what's <em>not</em> there. The "why nots" of the system.</p> <h3>A Recent Example</h3> <p>This one comes from <em>Logic for Programmers</em>. For convoluted technical reasons the epub build wasn't translating math notation (<code>\forall</code>) into symbols (<code>∀</code>). I wrote a script to manually go through and replace tokens in math strings with unicode equivalents. The easiest way to do this is to call <code>string = string.replace(old, new)</code> for each one of the 16 math symbols I need to replace (some math strings have multiple symbols).</p> <p>This is incredibly inefficient and I could instead do all 16 replacements in a single pass. But that would be a more complicated solution. So I did the simple way with a comment:</p> <div class="codehilite"><pre><span></span><code>Does 16 passes over each string BUT there are only 25 math strings in the book so far and most are <5 characters. So it's still fast enough. </code></pre></div> <p>You can think of this as a "why I'm using slow code", but you can also think of it as "why not fast code". It's calling attention to something that's <em>not there</em>.</p> <h3>Why the comment</h3> <p>If the slow code isn't causing any problems, why have a comment at all?</p> <div class="subscribe-form"></div> <p>Well first of all the code might be a problem later. If a future version of <em>LfP</em> has hundreds of math strings instead of a couple dozen then this build step will bottleneck the whole build. Good to lay a signpost now so I know exactly what to fix later.</p> <p>But even if the code is fine forever, the comment still does something important: it shows <em>I'm aware of the tradeoff</em>. Say I come back to my project two years from now, open <code>epub_math_fixer.py</code> and see my terrible slow code. I ask "why did I write something so terrible?" Was it inexperience, time crunch, or just a random mistake?</p> <p>The negative comment tells me that I <em>knew</em> this was slow code, looked into the alternatives, and decided against optimizing. I don't have to spend a bunch of time reinvestigating only to come to the same conclusion. </p> <h2>Why this can't be self-documented</h2> <p>When I was first playing with this idea, someone told me that my negative comment isn't necessary, just name the function <code>RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffs</code>. Aside from the issues of being long, not explaining the tradeoffs, and that I'd have to change it everywhere if I ever optimize the code... This would make the code <em>less</em> self-documenting. It doesn't tell you what the function actually <em>does</em>.</p> <p>The core problem is that function and variable identifiers can only contain one clause of information. I can't store "what the function does" and "what tradeoffs it makes" in the same identifier. </p> <p>What about replacing the comment with a test. I guess you could make a test that greps for math blocks in the book and fails if there's more than 80? But that's not testing <code>EpubMathFixer</code> directly. There's nothing in the function itself you can hook into. </p> <p>That's the fundamental problem with self-documenting negative information. "Self-documentation" rides along with written code, and so describes what the code is doing. Negative information is about what the code is <em>not</em> doing. </p> <h3>End of newsletter speculation</h3> <p>I wonder if you can think of "why not" comments as a case of counterfactuals. If so, are "abstractions of human communication" impossible to self-document in general? Can you self-document an analogy? Uncertainty? An ethical claim?</p> <div class="footnote"> <hr/> <ol> <li id="fn:exception"> <p>One interesting exception someone told me: they make code "more self-documenting" by turning comments into <em>logging</em>. I encouraged them to write it up as a blog post but so far they haven't. If they ever do I will link it here. <a class="footnote-backref" href="#fnref:exception" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 10 Sep 2024 19:40:29 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/why-not-comments/</guid> </item> <item> <title>Thoughts on "The Future of TLA+"</title> <link>https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/</link> <description><p>Last week Leslie Lamport posted <a href="https://lamport.azurewebsites.net/tla/future.pdf" target="_blank">The Future of TLA+</a>, saying "the future of TLA+ is in the hands of the TLA+ foundation". Lamport released TLA+ in 1999 and shepherded its development for the past 25 years. Transferring ownership of TLA+ to the official foundation has been in the works for a while now, and now it's time.</p> <p>In the document, Lamport also talks about some of his hopes for how the foundation will think about evolving TLA+. He gives three possible costs of any change:</p> <ol> <li>Breaking existing specifications</li> <li>Complicating the language. "Simplicity is a major goal of TLA+". </li> <li>Complicating the tooling. "There are few people contributing to the maintenance of the tools."</li> </ol> <p>With this, Lamport proposes removing several features, like octal numbers, submodules and tuple quantification.<sup id="fnref:tuple-quantification"><a class="footnote-ref" href="#fn:tuple-quantification">1</a></sup></p> <p>I would be sad if we lost tuple quantification, less so if we lost octal numbers. But what could be <em>added</em> to make TLA+ better? Features that don't just "save a few keystrokes" but significantly prove the expressiveness of our specifications.</p> <p>Some disclaimers. First, where I'm coming from: most of my contracts are either "get beginners started with TLA+" or "help a company write an exotic spec at the limit of what TLA+ can do." So I'm always thinking about how to teach the language better and how to abuse its most obscure features. Did you know about <a href="https://lamport.azurewebsites.net/tla/tla2-guide.pdf" target="_blank">labeled subexpressions</a>? You can do <em>magic</em> with labeled subexpressions.</p> <p>So I both want the skill floor lowered and the skill ceiling raised. This is often a contradictory goal.</p> <p>Second, these aren't official proposals or anything. I'm just sharing what I'd like to see, given infinite resources and no concerns about tradeoffs.</p> <p>Finally, most of these aren't changes to TLA+. They're for TLC.</p> <h3>TLA+ vs TLC</h3> <p>TLA+ is the specification language. TLC is the model checker used to check that specs written in that language satisfy certain properties. It's not the only tool; you could use <a href="https://proofs.tlapl.us/doc/web/content/Home.html" target="_blank">TLAPS</a> or <a href="https://apalache-mc.org/" target="_blank">Apalache</a> instead. But TLC is the most mature tool and the only one bundled with the standard distribution.</p> <p>This is a critical distinction for two reasons. One, TLC can only model-check a subset of TLA+. There are a set of four "forbidden operators" that cannot be used in any capacity (though the lead TLC developer [Markus Kuppe] is working on adding one). Lamport lists them in section 3.3 of his essay.</p> <p>Two, TLC makes some decisions about how to resolve some unspecified TLA+ operations. For example, <code>CHOOSE x \in set: P(x)</code> is undefined if no value in <code>set</code> satisfies <code>P</code> (<a href="https://lamport.azurewebsites.net/tla/book-02-08-08.pdf" target="_blank"><em>Specifying Systems</em></a> pg 73). In TLC, it's an error: </p> <blockquote> <p>Attempted to compute the value of an expression of form<br/> CHOOSE x in S: P, but no element of S satisfied P.</p> </blockquote> <p>This catches a lot of design bugs where you expected something to exist but it doesn't. But it also makes specifying defaults awkward. What if I want to get the record with a certain id, but if there isn't one, return a default value <code>NoRecord</code>? Then I have to write this:</p> <div class="codehilite"><pre><span></span><code>RetrieveRecord(id) == IF \E r \in records: r.id = id THEN CHOOSE r \in records: r.id = id ELSE NoRecord </code></pre></div> <p>If the condition is longer, this gets more cumbersome, and there's always a risk of them getting out of sync. </p> <p>The upside of TLC being separate from TLA+ is that we can add features to TLC without changing the overall semantics of TLA+. That's what Markus is doing in the <a href="https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tla2sany/StandardModules/TLC.tla" target="_blank">TLC module</a>: things like <code>TLCGet</code> and <code>Assert</code> adds new features to <em>model checking TLA+</em> without adding new syntax to TLA+. It should be possible to make a new operator <code>Try(Op, Fail)</code> that evaluates <code>Op</code> and, if it fails, returns <code>Fail</code> instead. Then we can just do</p> <div class="codehilite"><pre><span></span><code>RetrieveRecord(id) == Try(CHOOSE r \in records: r.id = id, NoRecord) </code></pre></div> <p>With that in mind, most of the things I want in TLA+ are actually things I want in TLC, with one exception. </p> <h2>Things I wished were in TLA+/TLC</h2> <h3>Try(Op, Fail)</h3> <p>It'd also be nice for equality checking. Right now <code>"2" = 2</code> raises an error, even if I want it to be false.</p> <p>(Could nested <code>Try</code>s lead to ambiguous behavior? Something to think about.)</p> <h3>A more obvious set filtering</h3> <p>This is the only change here that would affect TLA+ itself and not just TLC. This is map and filter in TLA+:</p> <div class="codehilite"><pre><span></span><code>\* Map {M(x): x \in set} \* Filter {x \in set: F(x)} </code></pre></div> <p>Beginners can never remember which is which. Even experienced people often forget which is which! I'd like a different keyword for each, so we could instead write</p> <div class="codehilite"><pre><span></span><code>\* Filter {x \in set WHERE F(x)} \* Mapfilter {M(x): x \in set WHERE F(x)} </code></pre></div> <p>Actual keyword may vary. </p> <h3>A linter</h3> <p>In the past few years TLA+ tooling has gotten a <em>lot</em> better. Among other things, there's now a <a href="https://github.com/tlaplus/vscode-tlaplus" target="_blank">debugger</a>, a <a href="https://github.com/tlaplus-community/tree-sitter-tlaplus" target="_blank">treesitter plugin</a>, and an <a href="https://will62794.github.io/tla-web/#!/home?specpath=.%2Fspecs%2FTwoPhase.tla" target="_blank">interactive spec explorer</a>. I hope we soon see a linter, something that could detect TLA+ snippets with valid-but-unexpected behavior. In particular, something that could detect this:</p> <div class="codehilite"><pre><span></span><code>/\ A /\ B => C </code></pre></div> <p>That extra space before <code>=></code> means its parsed as <code>A /\ (B => C)</code>, not as the expected <code>(A /\ B) => C</code>. I once lost a day to this. I know at least three other people who also lost a day to this. It is 100% valid TLA+ and we desperately need a tool to tell us when it happens because <em>my gosh</em> is it a morale-killer. </p> <div class="subscribe-form"></div> <h3>Support for the forbidden operators</h3> <p>There are four operators that are unsupported by TLC and virtually unused in any specs: <code>\cdot</code>, <code>\EE</code>, <code>\AA</code>, and <code>-+-></code>. I have no idea how to use <code>\cdot</code> but Markus is steadily adding support for it. <code>\AA</code> exists for completeness and even Lamport can't think of a good use for it. <code>-+-></code> could be useful (It's the LTL <a href="https://en.wikipedia.org/wiki/Linear_temporal_logic#Weak_until_and_strong_release" target="_blank">weak release</a>), but nobody's paid it any attention.</p> <p><code>\EE</code> is the interesting one, in that we know <em>exactly</em> how useful it would be. <code>\EE x: P(x)</code> is the <strong>temporal quantifier</strong> and is defined as "there exists a sequence of <code>x</code>s that make the temporal formula <code>P(x)</code> true for every step of the behavior. Critically, <code>x</code> can be a <em>different</em> value in each step, whereas <code>\E x: P(x)</code> would require the <em>same</em> value in each step.</p> <p>Why is this so important? One word: <a href="https://hillelwayne.com/post/refinement/" target="_blank">refinement</a>.</p> <p>Say we have an abstract server model and we want make a more detailed version of just the <em>server</em>. We can <em>refine</em> it by instantiating the abstract spec like this:</p> <div class="codehilite"><pre><span></span><code>Abstract == INSTANCE AbstractServer WITH abstract_var1 <- formula1, abstract_var2 <- formula2, \* ... Refinement == Abstract!Spec </code></pre></div> <p>See the above link for more details. The trouble is that we need to apply substitutions for <em>every</em> variable. If the abstract spec models both the server and the client, our refinement also needs to refine the client state, too! And more frustratingly, if the abstract spec has some kind of bookkeeping variable, say a <a href="https://learntla.com/core/functions.html#state-sweeping" target="_blank">state-sweeper</a> or <a href="https://learntla.com/topics/aux-vars.html" target="_blank">auxiliary variable</a>, you have to refine that, too.</p> <p>But TLA+ provides an out here. Using <code>\EE</code>, we can write the refinement like this:</p> <div class="codehilite"><pre><span></span><code>Abstract(aux_var, client_var) == INSTANCE AbstractServer WITH abstract_var1 <- formula1, abstract_var2 <- formula2, \* ... Refinement == \EE a, c: Abstract(a, c)!Spec </code></pre></div> <p>Lamport calls this <strong>variable hiding</strong> (<em>SS</em> pg 41).</p> <p>Now I don't know much about the internals of TLC, but my best guess is that <code>\EE</code> would be <em>extraordinarily</em> expensive to check. I think a regular refinement you can check as you go, since you can generate the full abstract transition at the same time you generate the main spec's transition. But with variable hiding, you only have information to create <em>part</em> of the abstract transition. I think the only way to do it would be to generate the abstract state graph first and then look for a transition that matches your current transition.</p> <p>That is <em>at the very least</em> an NP-Complete problem, one you have to compute for <em>every edge in your refinement graph</em>. But maybe there are some special cases (like aux variables that do not affect behavior) which would be cheaper to check?</p> <h3>\E-stealing</h3> <p>This one's both the most niche and probably the hardest to implement, so I don't expect to see it anytime soon. But this is <em>my</em> newsletter and it bothers <em>me</em> specifically.</p> <p>In <a href="https://hillelwayne.com/post/composing-tla/" target="_blank">Composing TLA+ Specifications with State Machines</a> I used this trick to compose two specs:</p> <div class="codehilite"><pre><span></span><code>Action1 == /\ state = "start" /\ DoThing /\ state' = "wait" Sync == LET i == << state, state' >> IN CASE i == << "start", "wait" >> -> SomeSyncRules [] << "wait, start" >> -> DifferentSyncRules </code></pre></div> <p>So far, so good. But what if I take a nondeterministic action?</p> <div class="codehilite"><pre><span></span><code>Action2 == /\ state = "wait" /\ \E x \in set: DoThing(x) /\ state' = "start" Sync == LET i == << state, state' >> IN CASE i == << "start", "wait" >> -> SomeSyncRules [] << "wait, start" >> -> ??? </code></pre></div> <p>There's no way for the body of <code>Sync</code> to <em>know</em> which value <code>Action2</code> picked for <code>x</code>. I've found <code>Sync</code> actions are a great way to compose specifications, and this "opaqueness" limits how strong it can be. It'd be great to have some way of side-channeling out what <code>x</code> was picked, without changing the essential semantics of <code>Action2</code>. Preferably without even needing to modify it.</p> <p>This might also be useful for <a href="https://arxiv.org/abs/2006.00915" target="_blank">test case generation</a>, since you can augment the output traces with more information. </p> <p>I have no idea what this would look like or how it would behave. It's just a pipe dream of mine.</p> <h3>Other pipe dreams</h3> <p>A better function update syntax. Some way to <a href="https://www.hillelwayne.com/post/tla-adt/#dynamic-stacks" target="_blank">partially refine one spec into multiple</a>, seamlessly upgrading their individual variables into function variables (without <a href="https://www.hillelwayne.com/post/tla-adt/#fixing-the-action" target="_blank">too much boilerplate</a>). <del>Being able to inline a config file in a spec so you don't need two separate files.</del>[^inlining] Being able to call tlc without a config file and passing in flags. Support for <code>-+-></code>, just for giggles. Clock variables. Non-latex ASCII syntax. UNCHANGED (all vars except <code><<a, b>></code>). Parallelized liveness checking. A tree that grants all my wishes.</p> <h2>[^inlining]: This is now provisionally possible! You can see an example <a href="https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/test-model/Github866.tla" target="_blank">here</a>, run it with <code>tlc -config Github866.tla Github866.tla</code></h2> <h3>Comment Section</h3> <p>Buttondown now has comments! I'm experimentally enabling it for now, so you should be able to comment on the <a href="" target="_blank">email's archive page</a>. Again, <em>this is experimental</em>. If I end up having to spend all my time community moderating, comments are going down again. Be nice to each other.</p> <div class="footnote"> <hr/> <ol> <li id="fn:tuple-quantification"> <p><code>CHOOSE << x, y >> \in set \X set: x < y</code>. <a class="footnote-backref" href="#fnref:tuple-quantification" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 04 Sep 2024 17:00:19 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/</guid> </item> <item> <title>State and time are the same thing</title> <link>https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/</link> <description><p style="height:16px; margin:0px !important;"></p> <h2>Time is state</h2> <p>Imagine I put an ordinary ticking <a href="https://en.wikipedia.org/wiki/Quartz_clock" target="_blank">quartz clock</a> in an empty room. I walk in, and ten minutes later I walk out with two photograph prints.<sup id="fnref:prints"><a class="footnote-ref" href="#fn:prints">1</a></sup> In the 1st one, the second hand is pointing at the top of the clock, in the 2nd it's pointing at the bottom. Are these two copies of the same photo, or are they two different pictures?</p> <p><img alt="A quartz clock with minute and second hands" class="newsletter-image" src="https://assets.buttondown.email/images/a6756666-c6f1-4ac7-9445-5ba6efa1406b.png?w=960&fit=max"/> </p> <p>There's no trick here, the answer is "different photos". Since the clock looks different, time must have passed between the two. More formally, we can represent the clock as a <strong>state vector</strong> of <code>(hours, minutes, seconds)</code>. Since the two pictures have different state vectors, they must represent different photos.</p> <p>Now I repeat the process and come out with two prints, both showing the same time. Are these two copies of the same photo or two different photos?</p> <p>It's <em>possible</em> for the pictures to be different but there's no way to be certain. I could have taken the two photos half a second apart so that time passed but the clock didn't tick yet. There's no observational difference between "time didn't pass" and "time passed but the state vector is the same". We can model time only passing in one second increments, as any shorter passage of time is not reflected in the state vector.</p> <p>Things would be different if you had access to the clock internals. The clock is powered by a quartz crystal that oscillates at approximately 2^15 hz, and a digital circuit inside the clock is counting that number as "one second". If you could read the memory inside the clock, then you could distinguish "00:00:15" and "00:00:15 + 2^14 oscillations".</p> <p>But in our current system that state is internal to the watch. Until the circuits turn the internal value into an observable value, we cannot recognize the passage of time for the clock.</p> <p>The only way we can see the passage of time is by measuring changes in observable state.</p> <div class="subscribe-form"></div> <h2>State is time</h2> <p>Pseudocode snippet:</p> <div class="codehilite"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>I'm trying to say that we pass a reference of a variable into <code>f</code>, so <code>int x = 1; f(x); print(x)</code> will output 3. Calling <code>f(x)</code> permanently splits our program into two eras. Before <code>f(x)</code>, all calculations with <code>x</code> will get one thing. <em>Anno Effexi</em>, all calculations with <code>x</code> will get another thing. The update to <code>x</code> advanced time. </p> <p>Now a more complicated case:</p> <div class="codehilite"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="o">++</span><span class="p">;</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="o">++</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>Does this advance time one step or two? Depends on whether the program is single-threaded or concurrent. If it's single threaded, when <code>f</code> executes there's nothing else that can read x in between the two updates, so the first mutation is "internal". Externally it looks like there was only one observable mutation <code>x += 2</code>. On the other hand, if the program is concurrent, it's possible for some other thread or w/e to read x in between the two statements of <code>f</code>. That makes the internal mutation observable, so time is advanced twice. There are now three eras of our program with different possible behaviors.</p> <p>Changes in state matter in that they create new times.</p> <h3>The consequences</h3> <p>Some function programmers stay "shared mutable state is the enemy". I think it's more like "time is the enemy", and time represents itself as mutable state. If a state update is <em>purely internal</em> and <em>cannot</em> affect the observable state (such as a statement in an uncommitted transaction), then it does not advance time.</p> <p>I find this a good model to reason about abstract systems. It also motivates formal methods concepts like bisimulation and <a href="https://hillelwayne.com/post/refinement/" target="_blank">refinement</a>, which I really need to do an updated post on.</p> <div class="footnote"> <hr/> <ol> <li id="fn:prints"> <p>I've got a GendankenWorks instant digital camera that takes digital pictures but lets me print them too. I'm sure <em>someone</em> sells something like this. <a class="footnote-backref" href="#fnref:prints" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 27 Aug 2024 18:13:37 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/</guid> </item> <item> <title>An idea for teaching formal methods better</title> <link>https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/</link> <description><p>I was recently commissioned by a company to make a bespoke <a href="https://www.learntla.com" target="_blank">TLA+</a> workshop with a strong emphasis on reading specifications. I normally emphasize <em>writing</em> specs, so this one will need a different approach.</p> <p>While working on it, I had an idea that <em>might</em> make teaching TLA+— and other <a href="https://buttondown.com/hillelwayne/archive/a-very-brief-intro-to-formal-methods-aka-my-job/" target="_blank">formal methods</a>— a little easier.</p> <h2>Pseudospecs</h2> <p>There are two problems to reading a spec:</p> <ol> <li>Learning the mental model of what TLA+ is actually <em>doing</em></li> <li>Learning how to actually read TLA+.</li> </ol> <p>The second problem is way, <em>way</em> bigger than it seems, because TLA+ has immense expressive power packed in a scary-looking syntax. Like take this spec:</p> <div class="codehilite"><pre><span></span><code>---- MODULE foo ---- CONSTANT Servers VARIABLE counters Init == counters = [s \in Servers |-> 0] Zero(s) == /\ counters[s] > 0 /\ counters' = [counters EXCEPT ![s] = 0] Inc(s) == counters' = [counters EXCEPT ![s] = @ + 1] Next == \/ \E s \in Servers: \/ Zero(s) \/ Inc(s) Spec == Init /\ [][Next]_counters ==== </code></pre></div> <p>What's <code>/\</code>? What's <code>![s]</code>? What's <code>[][Next]_counters</code>? To understand this spec, I have to <em>understand how to read TLA+</em>. This isn't an insurmountable obstacle, because otherwise <em>nobody</em> would know TLA+, but it could be the difference between "10 people learn TLA+" and "7 people learn TLA+". </p> <p>My idea is to provide the spec along with a pseudospec, which could look like this:</p> <div class="codehilite"><pre><span></span><code>Params {servers} Initialize with { counters: Map(s in Servers: 0) } action Zero(s) { requires { counters[s] > 0 } counters[s] := 0 } action Inc(s) { counters[s] +:= 1 } action Next { pick some s in Servers and { either do Zero(s) or do Inc(s) } </code></pre></div> <p>(Still working on what to do about the <code>Spec</code> operator.)</p> <p>Couple things to notice. One: it's not a consistent translation between two languages, but between language and meaning. There's no formal grammar for the pseudospec. I need inconsistency for reasons I'll talk about later. </p> <p>Two: the translation is not purely local. Things get moved around a little bit. <code>Zero</code> being an action affects how I translate <code>\E</code>.</p> <p>Three: the translation is kind of <a href="https://en.wikipedia.org/wiki/Metaphrase" target="_blank">metaphrasic</a>. It's close to, but not exactly, a line-by-line translation. A person who has both files open in a split can correlate the spec and pseudospec.<sup id="fnref:pluscal"><a class="footnote-ref" href="#fn:pluscal">1</a></sup></p> <p>Most directly, this will help people understand what the spec is doing, which gives me a basis to teach model-checking, safety properties, etc. But I think this will also makes learning the TLA+ syntax easier, by acting like an answer sheet. The student can read the TLA+ and, if they get stuck, look at the pseudospec for meaning. </p> <p>My moonshot hope is that this also helps with writing specs, by giving people a clearer sense of the standard TLA+ idioms. I can translate the same syntax slightly differently depending on the way it's used, and whether it's a general construct or one for just the spec. That might be doing too much with this concept, though. </p> <h2>Problems I see</h2> <div class="subscribe-form"></div> <p>First is that there's lots of nuances that would be lost in the translation. In writing</p> <div class="codehilite"><pre><span></span><code>Init == /\ x \in 1..5 --> Initialize with { x: pick some in 1..5 } </code></pre></div> <p>I lose the idea that <code>Init</code> <em>isn't special</em>, it's just a boolean operator like anything else. The student will have to unlearn that misconception if they ever need to do tricks like <a href="https://learntla.com/topics/optimization.html#ignore-part-of-the-state-space" target="_blank">FastInit</a>. </p> <p>This is an essential tradeoff in pedagogy: is it better to teach the full picture now or the easy thing first and fix the misconception later? The teacher side of me knows that the white lie is better in the long run, but the expert side of me <em>hates</em> this. </p> <p>Second, not every TLA+ symbol has a 1-1 mapping with English. I did the translation</p> <div class="codehilite"><pre><span></span><code>\E s \in Servers: \/ Zero(s) \/ Inc(s) --> pick some s in Servers and { either do Zero(s) or do Inc(s) } </code></pre></div> <p>That's because <code>Zero</code> and <code>Inc</code> are actions— things that update state. If they were regular operators, If P and Q are regular operators, I'd translate it like this:</p> <div class="codehilite"><pre><span></span><code>exists an s in S { P(s) || Q(s) } </code></pre></div> <p>Depending on the context, I'll translate <code>\E</code> in two different ways.</p> <p>This ties back to problem #1. In TLA+, <em>these are the same thing</em>. Performing an action is <em>literally</em> saying that it's a true description of the next-state relation. This has the consequence of <em>looking</em> like it updates the state relation, but that's only in certain (exceptionally common) circumstances. This is all incredibly elegant and intuitive to a TLA+ expert, but not for a beginner. So I have to introduce some inconsistencies. </p> <p>Problem #3 is just how much syntax I'll need to translate. What do I do for <code>[A -> B]</code>, <code>WF_vars</code>, or <code>\AA</code>? I can try to avoid them for the early specs but can't do that forever. </p> <p>Overall, though, I think the potential benefit of easier learning will outweigh the drawbacks.</p> <h3>Will this work?</h3> <p>We'll find out!</p> <div class="footnote"> <hr/> <ol> <li id="fn:pluscal"> <p>Experienced TLA+ users might notice that this is inspired by both PlusCal and <a href="https://github.com/informalsystems/quint" target="_blank">Quint</a>. I can't just teach those because the goal is to get people use TLA+ proper. (Also we really need a word for "experienced TLA+ user", like "Pythonista" or "Rustacean") <a class="footnote-backref" href="#fnref:pluscal" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 21 Aug 2024 16:05:27 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/</guid> </item> <item> <title>Texttools dot py</title> <link>https://buttondown.com/hillelwayne/archive/texttools-dot-py/</link> <description><p>I make a lot of personal software tools. One of these is "texttools.py", which is easiest to explain with an image:</p> <p><img alt="A GUI with the first three lines of this newsletter in the top, and the bottom has the three lines with markdown quote prefixes" class="newsletter-image" src="https://assets.buttondown.email/images/ddf06d0f-d8b9-4ae4-8c10-b275e104731e.png?w=960&amp;fit=max"/> </p> <p>Paste text in the top box, choose a transform, output appears in the bottom box. I can already do most of these transformations in vim, or with one of the many online tools out there, but I prefer my script for two reasons:</p> <ol> <li>There's no context switching. I don't have to leave my current tab or vim buffer and worry about cleanup later. One hotkey opens the GUI, <code>&lt;esc&gt;</code> closes it, with no break in my workflow.</li> <li>It loads in &lt;10 ms.</li> </ol> <p>It took me like an hour to make and I use it all the time. And it's small enough that I just share the whole script here.</p> <h2>How it works</h2> <p>Texttools is a python script running a <a href="https://docs.python.org/3/library/tkinter.html" target="_blank">tkinter</a> GUI. I used tkinter because it's a builtin; I would generally <em>not</em> recommend it if you have any better options. On the plus side, being a builtin means you don't need to install a package to use this yourself.</p> <div class="subscribe-form"></div> <div class="codehilite"><pre><span></span><code><span class="kn">import</span> <span class="nn">tkinter</span> <span class="k">as</span> <span class="nn">tk</span> <span class="kn">from</span> <span class="nn">tkinter</span> <span class="kn">import</span> <span class="n">N</span><span class="p">,</span> <span class="n">S</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">ttk</span> <span class="c1"># Complex transforms go here,</span> <span class="c1"># Simple transforms are just lambdas</span> <span class="k">def</span> <span class="nf">_wordcount</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="s2">"Returns a tuple of linecount, wordcount, charcount"</span> <span class="k">return</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">splitlines</span><span class="p">()),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">))</span> <span class="n">transforms</span> <span class="o">=</span> <span class="p">[</span> <span class="c1"># Transforms go here, for example</span> <span class="p">{</span><span class="s2">"name"</span><span class="p">:</span> <span class="s2">"One line"</span><span class="p">,</span> <span class="s2">"transform"</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">x</span><span class="o">.</span><span class="n">splitlines</span><span class="p">())}</span> <span class="p">,{</span><span class="s2">"name"</span><span class="p">:</span> <span class="s2">"Line/Word/Char"</span><span class="p">,</span> <span class="s2">"transform"</span><span class="p">:</span> <span class="n">_wordcount</span><span class="p">}</span> <span class="p">]</span> <span class="k">class</span> <span class="nc">GUI</span><span class="p">():</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Tk</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="c1"># start with no transform</span> <span class="bp">self</span><span class="o">.</span><span class="n">layout_gui</span><span class="p">()</span> <span class="k">def</span> <span class="nf">layout_gui</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span> <span class="o">=</span> <span class="n">ttk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s2">"3 3 12 12"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">"Text Tools"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">S</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">columnconfigure</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">rowconfigure</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Text</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">undo</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Text</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">undo</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Listbox</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">rowspan</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">transforms</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">,</span> <span class="n">t</span><span class="p">[</span><span class="s2">"name"</span><span class="p">])</span> <span class="c1"># Keyboard bindings</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Escape&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">quit</span><span class="p">())</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Tab&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">focus</span><span class="p">())</span> <span class="c1"># vvv makes clicking or pressing enter change the transform</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Button-1&gt;"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">select_transform</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Return&gt;"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">select_transform</span><span class="p">)</span> <span class="c1"># vvv makes anything typed in update the output</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Key&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">after</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">focus</span><span class="p">()</span> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">)</span> <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span><span class="p">(</span><span class="n">content</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> <span class="k">def</span> <span class="nf">select_transform</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">selection</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">curselection</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span> <span class="o">=</span> <span class="n">transforms</span><span class="p">[</span><span class="n">selection</span><span class="p">][</span><span class="s2">"transform"</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">()</span> <span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span> <span class="k">pass</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="c1"># Entry point for pyproject.toml</span> <span class="n">gui</span> <span class="o">=</span> <span class="n">GUI</span><span class="p">()</span> <span class="n">gui</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">mainloop</span><span class="p">()</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span> <span class="n">main</span><span class="p">()</span> </code></pre></div> <p>Man I forget how much I dislike tkinter until I have to look at it again. If you want to add your own text tools, just put a new item in the global <code>transforms</code> array.</p> <p>To make it easier to run the script, I put it in a "toolkit" repo with this <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/" target="_blank">pyproject.toml</a>:</p> <div class="codehilite"><pre><span></span><code><span class="k">[project]</span> <span class="n">name</span><span class="o">=</span><span class="s2">"toolkit"</span> <span class="n">version</span><span class="o">=</span><span class="s2">"0.0.1"</span> <span class="n">requires-python</span><span class="o">=</span><span class="s2">"&gt;=3.8"</span> <span class="k">[project.gui-scripts]</span> <span class="n">toolkit-texttools</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"toolkit.texttools:main"</span> </code></pre></div> <p>Then running <code>pip install -e .</code> creates a <code>toolkit-texttools</code> binary (or in my case a <code>.exe</code>).</p> <p>Finally, I wrote an <a href="https://www.hillelwayne.com/post/ahk-scripts-project/" target="_blank">AutoHotKey script</a> so I could load it with a keyboard shortcut:</p> <div class="codehilite"><pre><span></span><code><span class="c1">; &amp; numpagedown + t both pressed at same time</span> <span class="n">NumpadPgDn</span> <span class="o">&amp;</span> <span class="n">t</span><span class="o">::</span> <span class="n">toggle_app</span><span class="p">(</span><span class="s">"Text Tools"</span><span class="p">,</span> <span class="s">"toolkit-texttools.exe"</span><span class="p">)</span> </code></pre></div> <p>I like mapping stuff to the numpad because it's guaranteed to not interfere with any OS or program-specific hotkeys.</p> <p>Short newsletter this week because I'm still recovering from jetlag. See you all next week!</p></description> <pubDate>Wed, 14 Aug 2024 17:19:46 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/texttools-dot-py/</guid> </item> <item> <title>Why I prefer rST to markdown</title> <link>https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/</link> <description><p>I just published a new version of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>! v0.2 has epub support, content on constraint solving and formal specification, and more! Get it <a href="https://leanpub.com/logic/" target="_blank">here</a>.</p> <p>This is my second book written with <a href="https://www.sphinx-doc.org/en/master/" target="_blank">Sphinx</a>, after the new <a href="https://www.learntla.com/" target="_blank">Learn TLA+</a>. Sphinx uses a peculiar markup called <a href="https://docutils.sourceforge.io/rst.html" target="_blank">reStructured Text</a> (rST), which has a steeper learning curve than markdown. I only switched to it <em>after</em> writing a couple of books in markdown and deciding I needed something better. So I want to talk about why rst was that something.<sup id="fnref:rst"><a class="footnote-ref" href="#fn:rst">1</a></sup></p> <h2>Why rst is better</h2> <p>The most important difference between rst and markdown is that markdown is a lightweight representation of html, while rst is a midweight representation of an abstract documentation tree.</p> <p>It's easiest to see this with a comparison. Here's how to make an image in markdown:</p> <div class="codehilite"><pre><span></span><code> </code></pre></div> <p>Technically, you don't even need a parser for this. You just need a regex to transform it into <code>&lt;img alt="alttext" src="example.jpg"/&gt;</code>. Most modern markdown engines <em>do</em> parse this into an intermediate representation, but the <em>essence</em> of markdown is that it's a lightweight html notation.</p> <p>Now here's how to make an image in rst:</p> <div class="codehilite"><pre><span></span><code><span class="p">..</span> <span class="ow">image</span><span class="p">::</span> example.jpg <span class="nc">:alt:</span> alttext </code></pre></div> <p><code>.. image::</code> defines the image "directive". When Sphinx reads it, it looks up the registered handler for the directive, finds <code>ImageDirective</code>, invokes <code>ImageDirective.run</code>, which returns an <code>image_node</code>, which is an object with an <code>alt</code> field containing "alttext". Once Sphinx's processed all nodes, it passes the whole doctree to the HTML Writer, which looks up the rendering function for <code>image_node</code>, which tells it to output an <code>&lt;image&gt;</code> tag.</p> <p>Whew that's a mouthful. And for all that implementation complexity, we get… an interface that has 3x the boilerplate as markdown.</p> <p>On the other hand, the markdown image is hardcoded as a special case in the parser, while the rst image is not. It was added in the exact same way as every other directive in rst: register a handler for the directive, have the handler output a specific kind of node, and then register a renderer for that node for each builder you want.</p> <p>This means you can extend Sphinx with new text objects! Say you that instead of an <code>&lt;image&gt;</code>, you want a <code>&lt;figure&gt;</code> with a <code>&lt;figcaption&gt;</code>. In basic markdown you have to manually insert the html, with Sphinx you can just register a new <code>figure</code> directive. You can even make your <code>FigureDirective</code> subclass <code>ImageDirective</code> and have it do most of the heavy lifting.</p> <p>The second benefit is more subtle: you can transform the doctree before rendering it. This is how Sphinx handles cross-referencing: if I put a <code>foo</code> anchor in one document and <code>:ref:`image &lt;foo&gt;`</code> in another, Sphinx will insert the right URL during postprocessing. The transformation code is also first-class with the rest of the build process: I can configure a transform to only apply when I'm outputting html, have it trigger in a certain stage of building, or even remove a builtin transform I don't want to run.</p> <p>Now, most people may not need this kind of power! Markdown is ubiquitous because it's lightweight and portable, and rst is anything but. But <em>I</em> need that power.</p> <div class="subscribe-form"></div> <h3>One use case</h3> <p><em>Logic for Programmers</em> is a math-adjacent book, and all good math books need exercises for the reader. It's easier to write an exercise if I can put it and the solution right next to each other in the document. But for readers, I want the solutions to show up in the back of the book. I also want to link the two together, and since I might want to eventually print the book, the pdfs should also include page references. Plus they need to be rendered in different ways for latex (pdf) output and epub output. Overall lots of moving parts.</p> <p>To handle this I wrote my own exercise extension.</p> <div class="codehilite"><pre><span></span><code><span class="c">.. in chapter.rst</span> <span class="p">..</span> <span class="ow">exercise</span><span class="p">::</span> Fizzbuzz <span class="nc">:name:</span> ex-fizzbuzz An exercise <span class="p">..</span> <span class="ow">solution</span><span class="p">::</span> ex-fizzbuzz A solution <span class="c">.. in answers.rst</span> <span class="p">..</span> <span class="ow">solutionlist</span><span class="p">::</span> </code></pre></div> <p>How these nodes are processed depends on my compilation target. I like to debug in HTML, so for HTML it just renders the exercise and solution inline.</p> <p>When generating epub and latex, though, things works a little differently. After generating the whole doctree, I run a transform that moves every solution node from its original location to under <code>solutionlist</code>. Then it attaches a reference node to every exercise, linking it to the <em>new</em> solution location, and vice versa. So it starts like this (using Sphinx's "pseudoxml" format): </p> <div class="codehilite"><pre><span></span><code>--<span class="w"> </span>chapter.rst <span class="nt">&lt;exercise_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;title&gt;</span> <span class="w"> </span>Fizzbuzz <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>An<span class="w"> </span>exercise <span class="nt">&lt;solution_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>A<span class="w"> </span>solution --<span class="w"> </span>answers.rst <span class="nt">&lt;solutionlist_node&gt;</span> </code></pre></div> <p>And it becomes this:</p> <div class="codehilite"><pre><span></span><code>--<span class="w"> </span>chapter.rst <span class="nt">&lt;exercise_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;title&gt;</span> <span class="w"> </span>Fizzbuzz <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>An<span class="w"> </span>exercise <span class="w"> </span><span class="nt">&lt;exsol_ref_node</span><span class="w"> </span><span class="na">refuri=</span><span class="s">"/path/to/answers#ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span>Solution --<span class="w"> </span>answers.rst <span class="nt">&lt;solutionlist_node&gt;</span> <span class="w"> </span><span class="nt">&lt;solution_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>A<span class="w"> </span>solution <span class="w"> </span><span class="nt">&lt;exsol_ref_node</span><span class="w"> </span><span class="na">refuri=</span><span class="s">"/path/to/chapter#ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span>(back) </code></pre></div> <p style="height:16px; margin:0px !important;"></p> <p>The Latex builder renders this by wrapping each exercise and solution in an <a href="https://ctan.org/pkg/exercise" target="_blank">answers environment</a>, while the epub builder renders the solution as a <a href="https://help.apple.com/itc/booksassetguide/en.lproj/itccf8ecf5c8.html" target="_blank">popup footnote</a>.<sup id="fnref:exsol_ref"><a class="footnote-ref" href="#fn:exsol_ref">2</a></sup> Making this work:</p> <p><img alt="An example of solution popups on an epub reader" class="newsletter-image" src="https://assets.buttondown.email/images/8a7d66e3-56bd-4b7a-95ac-d4fdf88047c7.png?w=960&amp;fit=max"/> </p> <p>It's a complex dance of operations, but it works enormously well. It even helps with creating a "free sample" subset of the book: the back of the free sample only includes the solutions from the included subset, not the whole book!</p> <h3>"But I hate the syntax"</h3> <p>When I gush about rST to other programmers, this is the objection I hear the most: it's ugly. </p> <p>To which I say, are you really going to avoid using a good tool just because it makes you puke? Because looking at it makes your stomach churn? Because it offends every fiber of your being?</p> <p>...Okay yeah that's actually a pretty good reason not to use it. I can't get into lisps for the same reason. I'm not going to begrudge anybody who avoids a tool because it's ugly.</p> <p>Maybe you'd find <a href="https://github.com/asciidoctor/asciidoctor" target="_blank">asciidoc</a> more aesthetically pleasing? Or <a href="https://mystmd.org/spec" target="_blank">MyST</a>? Or <a href="https://github.com/typst/typst" target="_blank">Typst</a>? Or <a href="https://docs.racket-lang.org/pollen/" target="_blank">Pollen</a>? Or even <a href="https://pandoc.org/MANUAL.html#extension-attributes" target="_blank">pandoc-extended markdown</a>? There are lots of solid document builders out there! My point isn't that sphinx/rst is exceptionally <em>good</em> for largescale documentation, it's that simple markdown is exceptionally <em>bad</em>. It doesn't have a uniform extension syntax or native support for pre-render transforms.</p> <p>This is why a lot of markdown-based documentation generators kind of hack on their own preprocessing step to support new use-cases, which works for the most part (unless you're trying to do something really crazy). But they have to work around the markdown, not in it, which limits how powerful they can be. It also means that most programmer tooling can't understand it well. There's LSP and treesitter for markdown and rst but not for gitbook-markdown or md-markdown or leanpub-markdown.<sup id="fnref:treesitter"><a class="footnote-ref" href="#fn:treesitter">3</a></sup></p> <p>But if you find a builder that uses markdown and satisfies your needs, more power to you! I just want to expose people to the idea that doc builders can be a lot more powerful than they might otherwise expect.</p> <hr/> <h3>No newsletter next week</h3> <p>I'll be in Hong Kong.</p> <h2>Update 2024-07-31</h2> <p>Okay since this is blowing up online I'm going to throw in a quick explanation of <em>Logic for Programmers</em> for all of the non-regulars here. I'm working on a book about how formal logic is useful in day-to-day software engineering. It starts with a basic rundown of the math and then goes into eight different applications, such as property testing, database constraints, and decision tables. It's still in the alpha stages but already 20k words and has a lot of useful content. You can find it <a href="https://leanpub.com/logic" target="_blank">here</a>. Reader feedback highly appreciated!</p> <div class="footnote"> <hr/> <ol> <li id="fn:rst"> <p>rst is actually independent of Sphinx, but everybody I know who writes rst writes it <em>because</em> they're using Sphinx, so I'll use the two interchangeably. Also typing rST is annoying so I'm typing rst. <a class="footnote-backref" href="#fnref:rst" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:exsol_ref"> <p>This is why I attach <code>exsol_ref_nodes</code> and not default <code>reference_nodes</code>. Sphinx's epub translator uses an attribute passlist I need to workaround in post-rendering. <a class="footnote-backref" href="#fnref:exsol_ref" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:treesitter"> <p>This is also one place where rst's ugly syntax works in its favor. I've got a treesitter query that changes the body of todo directives and <em>only</em> todo directives, which is only possible because the rst syntax tree is much richer than the markdown syntax tree. <a class="footnote-backref" href="#fnref:treesitter" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Wed, 31 Jul 2024 15:34:39 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/</guid> </item> <item> <title>My patented Miracle Tonic would have prevented the CrowdStrike meltdown</title> <link>https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/</link> <description><p>Last Friday CrowdStrike did something really bad and it destroyed every airport in the world. I didn't bother to learn anything else about it because I was too busy writing my 10k whitepaper about how all the problems were all caused by one simple mistake: not drinking my patented Miracle Tonic™®.</p> <p>Developers who drink my Miracle Tonic write code 100% faster with 100% fewer bugs. This would have prevented the CrowdStrike outage, the 2016 DNC hack, Ariane 5, Therac-25, and that one moth caught in the Harvard Mark II. Developers are also happier at work, suffer no burnout, and keep all standups to <em>exactly</em> five minutes.</p> <p>The Miracle Tonic is so effective that it should be <em>immoral</em> not to drink it. It's like if surgeons didn't wash their hands. If you write code for a living and you don't drink my Miracle Tonic, do us all a favor and never touch a computer again. You idiot. You absolute moron. I can't believe you call yourself a "professional".</p> <p><strong>Frequently Asked Questions:</strong></p> <blockquote> <p>Do you have any actual evidence that Miracle Tonic actually helps programmers?</p> </blockquote> <p>Yes I do! All sorts of studies prove the effectiveness of Miracle Tonic:</p> <ol> <li>One survey found that 68% of devs drinking miracle tonic say their code is "awesome". That means it works!</li> <li>A <em>double-blind clinical trial</em> found that 7 undergraduates who were given Miracle Tonic wrote 10% better code (using the Hills-Bourne-Davidio Corrected Dolorimetry metric) than the control group (6 undergraduates who were punched in the face).</li> <li>Someone found twelve projects on GitHub that didn't use Miracle Tonic and they had 268% worse failure rates than this one project I did with it. That's a P value of 0.00004!</li> </ol> <p>That's so many studies! I can say with 100% confidence that Miracle Tonic is proven to work beyond a shadow of a doubt.</p> <blockquote> <p>I read a study saying that Miracle Tonic gives people headaches.</p> </blockquote> <p>Why are you trusting <em>studies</em>? Are you really gonna listen to some graduate student dweeb who's never been a <em>real programmer</em>?! If you're curious about Miracle Tonic, just try it and see for yourself.</p> <blockquote> <p>Are there any downsides to drinking Miracle Tonic?</p> </blockquote> <p>Of course, there is no silver bullet, everything is tradeoffs, etc etc etc. The downside of Miracle Tonic is it doesn't work if your company is a toxic feature factory that cares more about micromanaging mindless worker drones than cultivating good engineers. And don't drink it if you're allergic to peanuts.</p> <blockquote> <p>This tastes revolting.</p> </blockquote> <p>I've trained five Miracle Tonic Brand Ambassadors and they all tell me it's delicious. Your taste buds must be wrong.</p> <blockquote> <p>I tried drinking your Miracle Tonic and it didn't make me a better programmer.</p> </blockquote> <p><em>How dare you</em>. How dare you spread FUD about the most important programming technology ever made. Were you drinking exactly 12.063 mL at 67° C every 75 minutes? Yeah, thought not. Of course it's not going to work if you didn't follow it properly. And you'd know this if you took my $1500 three-day "how do drink Miracle Tonic" course. Which you didn't. <em>Get out of my sight.</em></p> <blockquote> <p>How does Miracle Tonic compare to competing products, like toad oil, Pirelli's Miracle Elixir, or Design-by-Contract?</p> </blockquote> <p>Fucking charlatans, all of them.</p> <hr/> <p>This is the part of my job I dread.</p> <p>I do formal methods for a living. Last year 100% of my income came from either writing specifications or training others how to write specifications. This year, due to a lot of work in diversifying my revenue streams, it will only be 90% of my income. This creates an immense pressure to become an ambulance chaser, to see ten billion dollars in economic damage as a chance to say "look at me!", a longshot chance to find the next source who'll pay my next month's rent.</p> <p>I'm also a True Believer. Of course formal methods would have prevented the CrowdStrike outage! It also would have prevented COVID-19, the Cuban Missile Crisis and the Lincoln assassination. Years of being an advocate have shaped my worldview to <em>always</em> see how it could have helped, how everything is just the right shape nail for the hammer I'm selling you.</p> <p>None of this depends on anything particular to formal methods, which is why advocates of every stripe are telling us "what they should have done", because every advocate is a true believer, and every advocate wants to get paid. I understand this all, and I get why people do this, but I hate feeling this way, that all this misery begets opportunity. I hate the pressure to say "they needed X" as fast as possible, before people lose interest, regardless of whether X would help or even if they were <em>already using</em> X. I hate the feeling that capitalizing on this might compromise my principles.</p> <p>Most of all though, I fear the slippery slope from advocating to grifting. There are people out there who saw the outage and got <em>excited</em>. Please, God, let me never be that.</p></description> <pubDate>Tue, 23 Jul 2024 15:22:21 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/</guid> </item> <item> <title>Keep perfecting your config</title> <link>https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/</link> <description><p>First of all, I wanted to extend a huge and heartfelt thank you to all of the people who bought <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>. Seeing the interest is incredible motivation to continue improving it. If you read it and have feedback, please share it with me!</p> <p>Second, I have a new blogpost out: <a href="https://www.hillelwayne.com/post/toolbox-languages/" target="_blank">Toolbox Languages</a>. It's about five obscure languages I use that make certain hard problems I have much easier. I hope it encourages other people to talk about their toolbox languages (or share new ones with me!). Patreon blog notes (and the cut sixth language) <a href="https://www.patreon.com/posts/108180681" target="_blank">here</a>.</p> <p>Third, actual newsletter. </p> <h1>Keep perfecting your config</h1> <p>Last week I read the article <a href="https://arkadiuszchmura.com/posts/stop-perfecting-your-config/" target="_blank">stop perfecting your config</a>, which argues that people spend too much time tinkering with "editors, IDEs, terminals [and] other applications". I firmly believe the opposite, that people should spend <em>more</em> time tinkering. To be fair, the OP says</p> <blockquote> <p>Before I continue, let’s make one thing clear. By no means am I saying that there is something wrong with trying to master your tools or attempting to adjust them to suit your specific needs. It’s actually the opposite.</p> </blockquote> <p>So I read this more as "don't make my mistakes" than "don't do what I don't like."<sup id="fnref:caveat"><a class="footnote-ref" href="#fn:caveat">1</a></sup> At the same time, it's notable are "his mistakes"— what he counted as perfecting his configs. Reading the article, he mentions Neovim keymaps once, plugins once, and color schemes four times. </p> <p>There's an idea in programming language design called <a href="https://wiki.haskell.org/Wadler's_Law" target="_blank">Wadler's Law</a>, which is that the majority of discussion is on syntax and not semantics. I imagine this applies to configuration too: the majority of time spent "tinkering" is on layout and coloring and not on changes to the tool's behavior. <strong>This is a trap.</strong> You get the most value of "configuration" when you use it to improve your workflow.</p> <p>Here's a Neovim script I wrote a while back:</p> <div class="codehilite"><pre><span></span><code><span class="kr">function</span> <span class="nf">LoadLocal</span><span class="p">(</span><span class="n">local_task</span><span class="p">)</span> <span class="n">vim</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">local_task</span> <span class="o">=</span> <span class="n">local_task</span> <span class="kr">end</span> <span class="kr">function</span> <span class="nf">RunLocal</span><span class="p">()</span> <span class="n">vim</span><span class="p">.</span><span class="n">cmd</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">local_task</span><span class="p">)</span> <span class="kr">end</span> <span class="n">vim</span><span class="p">.</span><span class="n">cmd</span> <span class="s">[[command! -nargs=1 LoadLocal call v:lua.LoadLocal(&lt;f-args&gt;)]]</span> <span class="n">vim</span><span class="p">.</span><span class="n">keymap</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'n'</span><span class="p">,</span> <span class="s1">'gxl'</span><span class="p">,</span> <span class="n">RunLocal</span><span class="p">,</span> <span class="p">{</span><span class="n">desc</span><span class="o">=</span><span class="s2">"Buffer Task"</span><span class="p">})</span> </code></pre></div> <p>On calling <code>:LoadLocal foo</code> it loads <code>foo</code> as the local command for that specific buffer. Then, typing <code>gxl</code> executes <code>foo</code>. The command can be <em>anything</em>, up to and including shelling out to <code>!rm *</code>.</p> <p>Oh, also any other script can set <code>vim.b.local_task</code>.<sup id="fnref:exploit"><a class="footnote-ref" href="#fn:exploit">2</a></sup> <code>LoadLocal</code> can be used by the rest of my configs. When I open an XML file it sets the local task to the appropriate transformation script.</p> <p>Those eight lines have probably saved me at least eight hours of drudgework over their lifespan. <a href="https://www.hillelwayne.com/post/task-runner-neovim/" target="_blank">The full taskrunner has saved even more</a>. <em>That's</em> the kind of customization that's most useful.</p> <p>Anything that goes for an IDE goes double for the shell. I use a ton of shell scripts. </p> <h3>Customization teaches you</h3> <div class="subscribe-form"></div> <p>A common argument against customization is that you shouldn't do it until you know the tooling basics well. For example, you shouldn't install a filetree plugin for neovim because it comes with Netrw. In other words, there's a difference between the skill of using your tools and the skill of customizing them, and the first is more important than the second.</p> <p>But just as often customization promotes mastery, by smoothing out the starting friction with using a feature. Say you're trying to get more comfortable with vim macros. Here's a keymapping that could help:</p> <div class="codehilite"><pre><span></span><code><span class="n">vim</span><span class="p">.</span><span class="n">keymap</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'n'</span><span class="p">,</span> <span class="s2">"&lt;leader&gt;Q"</span><span class="p">,</span> <span class="s">[[:let @q = input("Edit macro:", @q)&lt;CR&gt;]]</span><span class="p">)</span> </code></pre></div> <p>Vim macros are stored in a text registry: recording the <code>@q</code> macro will overwrite whatever you copied to the <code>"q</code> register. This means that you can treat a macro <em>as</em> text. <code>input</code> will fill a prompt with the <code>@q</code> macro text, let you edit it as text, and then save your changes back to the register.</p> <p>This makes it so much easier to experiment with macros! If you screw up a macro while recording, you don't need to start over, you can just finish and make an edit afterwards. If you try a finished macro and it does something wrong, same thing, just undo and edit it.</p> <h3>Reasons not to customize</h3> <p>I've heard two other reasons why <em>not</em> to heavily customize your system:</p> <ol> <li>It'll be harder for other people to pair with you on your machine</li> <li>It'll be harder for you to work on machines without the customizations: servers you SSHed into, VMs, other people's computers, etc.</li> </ol> <p>(1) is only a problem if you're modifying default keybindings or adding surprising new ones. I deal with it by having two editors: a heavily customized Neovim for solo work, a vanilla VSCode (plus plugins) for collaboration.</p> <p>(2) is a bigger problem. I find the problem is how it messes with muscle memory. In vim I've mapped <code>H/L</code> to beginning/end of line when normally they mean "first/last line in view", and it always screws me up. It is so immensely frustrating. Other than that, though, I <em>generally</em> can tolerate losing my config for a bit. Most of the stuff that's most useful to me are things that aren't useful when I'm remoting, so I don't miss them. </p> <p>One good reason not to customize that I <em>haven't</em> heard: There is one more reason I've heard not to customize: you can easily break stuff that you don't know how to fix. This is more true with stuff like vim and shell that allow arbitrary scripts as config, less so with VSCode or the new crop of modal editors.</p> <div class="footnote"> <hr/> <ol> <li id="fn:caveat"> <p>Also <a href="https://arkadiuszchmura.com/posts/optimizing-your-workflow-does-matter-in-the-long-run/" target="_blank">he wrote positively about configuration later</a>. <a class="footnote-backref" href="#fnref:caveat" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:exploit"> <p>I swear to God if this leads to someone targeting an exploit at me I will quit programming forever <a class="footnote-backref" href="#fnref:exploit" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description> <pubDate>Tue, 16 Jul 2024 19:18:22 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/</guid> </item> <item> <title>Logic for Programmers now in early access!</title> <link>https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/</link> <description><p>I am delighted to announce that <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a> is now available for purchase! While still in early access, it's almost 20,000 words, has 30 exercises, and covers a wide variety of logic applications: </p> <ul> <li>Property testing</li> <li>Functional correctness and contracts</li> <li>Formal verification</li> <li>Decision tables</li> <li>Database constraints</li> <li>Data modeling and alloy</li> <li>Constraint solving</li> <li>Logic programming</li> </ul> <p>I want to emphasize the book is early access; meaning it's not close to done yet. I'm releasing it now so I can get feedback from readers and use that to decide what I write next. But I'm only willing to release it because I think, even in its current state, it's worth reading. I'm just promising it'll be even better in a few months. </p> <h2>What's next</h2> <p>I think exercise solutions are broken on epub, so step one is getting that fixed. </p> <p>After that, I'm taking a short break. I put basically all of June into working on this and need to get back to blogging and client acquisition. Plus I need a bit of time to gather reader feedback. </p> <p>I'm thinking of new major releases being either every two weeks or every month or so. I'll upload new versions of the book between releases and then send a Leanpub publication announcement when we hit the next milestone.</p> <h3>PS</h3> <p>There's a free book coupon on the <a href="https://www.patreon.com/hillelwayne" target="_blank">Patreon</a>. Enough people have joined that I feel obligated to start posting more original content there <em>why does this keep happening to me</em></p></description> <pubDate>Mon, 08 Jul 2024 17:33:18 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/</guid> </item> <item> <title>Solving a math problem with planner programming</title> <link>https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/</link> <description><p>The deadline for the <a href="https://buttondown.email/hillelwayne/archive/logic-for-programmers-update/" target="_blank">logic book</a> is coming up! I'm hoping to have it ready for early access by either the end of this week or early next week. During a break on Monday I saw this interesting problem on <a href="https://math.stackexchange.com/questions/4939319/how-many-steps-are-needed-to-turn-one-a-into-at-least-100-000-as-using-only" target="_blank">Math Stack Exchange</a>:</p> <blockquote> <p>Suppose that at the beginning there is a blank document, and a letter "a" is written in it. In the following steps, only the three functions of "select all", "copy" and "paste" can be used. </p> <p>Find the minimum number of steps to reach at least 100,000 a's (each of the three operations of "select all", "copy" and "paste" is counted as one step). If the target number is not specified, and I want to get the exact amount of a, is there a general formula?</p> </blockquote> <p>The first two answers look for analytic solutions. The <a href="https://math.stackexchange.com/a/4939673" target="_blank">last answer</a> shares a C++ program that finds it via breadth-first search. I'll reproduce it here:</p> <div class="codehilite"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;iostream&gt;</span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;queue&gt;</span> <span class="k">enum</span><span class="w"> </span><span class="nc">Mode</span> <span class="p">{</span> <span class="w"> </span><span class="n">SELECT</span><span class="p">,</span> <span class="w"> </span><span class="n">COPY</span><span class="p">,</span> <span class="w"> </span><span class="n">PASTE</span> <span class="p">};</span> <span class="k">struct</span><span class="w"> </span><span class="nc">Node</span> <span class="p">{</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">noOfAs</span><span class="p">;</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">steps</span><span class="p">;</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">noOfAsCopied</span><span class="p">;</span> <span class="w"> </span><span class="n">Mode</span><span class="w"> </span><span class="n">mode</span><span class="p">;</span> <span class="p">};</span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="w"> </span><span class="n">q</span><span class="p">;</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">SELECT</span><span class="p">});</span> <span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">q</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">Node</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">front</span><span class="p">();</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">100000</span><span class="p">)</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="n">mode</span><span class="p">)</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">SELECT</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">COPY</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">COPY</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">PASTE</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">PASTE</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">SELECT</span><span class="p">});</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">PASTE</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>This is guaranteed to find a shortest possible solution due to a fun property of BFS: the distance of nodes to the origin never decreases. If you evaluate Node Y after Node X, then Y.dist &gt;= X.dist, meaning that the first valid solution will be a shortest possible solution. I should make this into a logic book example! </p> <p>This also has the drawback of preventing <a href="https://www.hillelwayne.com/post/cleverness/" target="_blank">the use of an insight</a>. We <em>should</em> be able to fuse the select and copy steps together, meaning instead of having three actions (select, copy, paste) we only need two (selectcopy, paste), where selectcopy takes twice as many steps as pasting.</p> <p>But we can't make that optimization because it breaks monotonicity. We're now pushing a mix of <code>n+1</code> and <code>n+2</code> steps onto the queue, and there's no way to order things to guarantee all of the <code>n+1</code> steps are searched before any <code>n+2</code> step.</p> <p>I thought I'd try to solve it with planning language instead, so we can get both the elegant solution and the optimization.</p> <h3>Planning</h3> <div class="subscribe-form"></div> <p>The rough idea of planning is that you provide an initial state, a set of actions, and a target, and the tool finds the shortest sequence of actions that reaches the target. I've written about it in-depth <a href="https://www.hillelwayne.com/post/picat/" target="_blank">here</a> and also a comparison of planning to model checking <a href="https://www.hillelwayne.com/post/picat/" target="_blank">here</a>. I like how some tough problems in imperative and functional paradigms become easy problems with planning. </p> <p>This is all in <a href="http://picat-lang.org/" target="_blank">Picat</a>, by the way, which I've talked about more <a href="https://buttondown.email/hillelwayne/archive/picat-is-my-favorite-new-toolbox-language/" target="_blank">here</a> and in the <a href="https://www.hillelwayne.com/post/picat/" target="_blank">planning piece</a>. I'll just be explaining the planning stuff specific to this problem.</p> <div class="codehilite"><pre><span></span><code><span class="s s-Atom">import</span> <span class="s s-Atom">planner</span><span class="p">.</span> <span class="s s-Atom">import</span> <span class="s s-Atom">util</span><span class="p">.</span> <span class="s s-Atom">main</span> <span class="s s-Atom">=&gt;</span> <span class="nv">Init</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">% one a, nothing copied</span> <span class="p">,</span> <span class="nf">best_plan</span><span class="p">(</span><span class="nv">Init</span><span class="p">,</span> <span class="nv">Plan</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="p">,</span> <span class="s s-Atom">nl</span> <span class="p">,</span> <span class="nf">printf</span><span class="p">(</span><span class="s2">"Cost=%d%n"</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="p">,</span> <span class="nf">printf</span><span class="p">(</span><span class="s2">"Plan=%s%n"</span><span class="p">,</span> <span class="nf">join</span><span class="p">([</span><span class="nv">P</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="s s-Atom">:</span> <span class="nv">P</span> <span class="s s-Atom">in</span> <span class="nv">Plan</span><span class="p">],</span> <span class="s2">" "</span><span class="p">))</span> <span class="p">.</span> </code></pre></div> <p>We're storing the state of the system as two integers: the number of characters printed and the number of characters on our clipboard. Since we'll be fusing selects and copies, we don't need to also track the number of characters selected <del>(unlike the C++)</del>.</p> <div class="codehilite"><pre><span></span><code><span class="nf">final</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="k">_</span><span class="p">))</span> <span class="s s-Atom">=&gt;</span> <span class="nv">A</span> <span class="o">&gt;=</span> <span class="mf">100000.</span> <span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">NewA</span> <span class="o">=</span> <span class="nv">A</span> <span class="o">+</span> <span class="nv">Clipboard</span> <span class="p">,</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">NewA</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"P"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">.</span> </code></pre></div> <p>The paste action just adds the clipboard to the character count. Because Picat is a research language it's a little weird with putting expressions inside structures. If we did <code>$state(1 + 1)</code> it would store it as <em>literally</em> <code>$state(1 + 1)</code>, not <code>state(2)</code>.</p> <p>Also you have to use dollar signs for definitions but no dollar signs for pattern matching inside a function definition. I have <em>no idea</em> why.</p> <div class="codehilite"><pre><span></span><code><span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">A</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"SC"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">2</span> <span class="p">.</span> </code></pre></div> <p>And that's it! That's the whole program. Running this gives us:</p> <div class="codehilite"><pre><span></span><code>Cost=42 Plan=SC P P SC P P SC P P SC P P SC P P SC P P SC P P SC P P SC P P P SC P P P </code></pre></div> <p>To find if there's a sequence that gets us <em>exactly</em> 100,000, we just need to make one change:</p> <div class="codehilite"><pre><span></span><code><span class="gd">- final(state(A, _)) =&gt; A &gt;= 100000.</span> <span class="gi">+ final(state(A, _)) =&gt; A = 100000.</span> </code></pre></div> <p>This returns a cost of 43.</p> <p>On the other hand, I can't get it to find a path that makes exactly <code>100,001</code> characters, even with some optimizations. This is because the shortest path is over 9000 steps long! I haven't checked if the C++ BFS can find it.</p> <h3>Metaplanning</h3> <p>One reason planning fascinates me so much is that, if a problem is now easy, you can play around with it. Like if I wanted to add "delete a character" as a move, that's easy:</p> <div class="codehilite"><pre><span></span><code><span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">A</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">,</span> <span class="nv">NewA</span> <span class="o">=</span> <span class="nv">A</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">,</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">NewA</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"D"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">.</span> </code></pre></div> <p>This doesn't make exceeding or reaching 100,000 easier, but it makes reaching 100,001 take 47 steps instead of 9000.</p> <p>With some tweaks, I can also ask questions like "what numbers does it make <em>the most</em> easier?" or "Do some numbers have more than one shortest path? Which number has the most?"</p> <p>Planning is really cool.</p></description> <pubDate>Tue, 02 Jul 2024 15:46:13 +0000</pubDate> <guid>https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/</guid> </item> </channel> </rss>
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Computer Things</title><link>https://buttondown.com/hillelwayne</link><description>Hi, I'm Hillel. This is the newsletter version of [my website](https://www.hillelwayne.com). I post all website updates here. I also post weekly content just for the newsletter, on topics like * Formal Methods * Software History and Culture * Fringetech and exotic tooling * The philosophy and theory of software engineering You can see the archive of all public essays [here](https://buttondown.email/hillelwayne/archive/).</description><atom:link href="https://buttondown.email/hillelwayne/rss" rel="self"/><language>en-us</language><lastBuildDate>Tue, 01 Apr 2025 16:04:59 +0000</lastBuildDate><item><title>[April Cools] Gaming Games for Non-Gamers</title><link>https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/</link><description><p>My <em>April Cools</em> is out! <a href="https://www.hillelwayne.com/post/vidja-games/" target="_blank">Gaming Games for Non-Gamers</a> is a 3,000 word essay on video games worth playing if you've never enjoyed a video game before. <a href="https://www.patreon.com/posts/blog-notes-gamer-125654321?utm_medium=clipboard_copy&amp;utm_source=copyLink&amp;utm_campaign=postshare_creator&amp;utm_content=join_link" target="_blank">Patreon notes here</a>.</p> <p>(April Cools is a project where we write genuine content on non-normal topics. You can see all the other April Cools posted so far <a href="https://www.aprilcools.club/" target="_blank">here</a>. There's still time to submit your own!)</p> <a class="embedded-link" href="https://www.aprilcools.club/"> <div style="width: 100%; background: #fff; border: 1px #ced3d9 solid; border-radius: 5px; margin-top: 1em; overflow: auto; margin-bottom: 1em;"> <div style="float: left; border-bottom: 1px #ced3d9 solid;"> <img class="link-image" src="https://www.aprilcools.club/aprilcoolsclub.png"/> </div> <div style="float: left; color: #393f48; padding-left: 1em; padding-right: 1em;"> <h4 class="link-title" style="margin-bottom: 0em; line-height: 1.25em; margin-top: 1em; font-size: 14px;"> April Cools' Club</h4> </div> </div></a></description><pubDate>Tue, 01 Apr 2025 16:04:59 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/</guid></item><item><title>Betteridge's Law of Software Engineering Specialness</title><link>https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/</link><description><h3>Logic for Programmers v0.8 now out!</h3> <p>The new release has minor changes: new formatting for notes and a better introduction to predicates. I would have rolled it all into v0.9 next month but I like the monthly cadence. <a href="https://leanpub.com/logic/" target="_blank">Get it here!</a></p> <h1>Betteridge's Law of Software Engineering Specialness</h1> <p>In <a href="https://agileotter.blogspot.com/2025/03/there-is-no-automatic-reset-in.html" target="_blank">There is No Automatic Reset in Engineering</a>, Tim Ottinger asks:</p> <blockquote> <p>Do the other people have to live with January 2013 for the rest of their lives? Or is it only engineering that has to deal with every dirty hack since the beginning of the organization?</p> </blockquote> <p><strong>Betteridge's Law of Headlines</strong> says that if a journalism headline ends with a question mark, the answer is probably "no". I propose a similar law relating to software engineering specialness:<sup id="fnref:ottinger"><a class="footnote-ref" href="#fn:ottinger">1</a></sup></p> <blockquote> <p>If someone asks if some aspect of software development is truly unique to just software development, the answer is probably "no".</p> </blockquote> <p>Take the idea that "in software, hacks are forever." My favorite example of this comes from a different profession. The <a href="https://en.wikipedia.org/wiki/Dewey_Decimal_Classification" target="_blank">Dewey Decimal System</a> hierarchically categorizes books by discipline. For example, <em><a href="https://www.librarything.com/work/10143437/t/Covered-Bridges-of-Pennsylvania" target="_blank">Covered Bridges of Pennsylvania</a></em> has Dewey number <code>624.37</code>. <code>6--</code> is the technology discipline, <code>62-</code> is engineering, <code>624</code> is civil engineering, and <code>624.3</code> is "special types of bridges". I have no idea what the last <code>0.07</code> means, but you get the picture.</p> <p>Now if you look at the <a href="https://www.librarything.com/mds/6" target="_blank">6-- "technology" breakdown</a>, you'll see that there's no "software" subdiscipline. This is because when Dewey preallocated the whole technology block in 1876. New topics were instead to be added to the <code>00-</code> "general-knowledge" catch-all. Eventually <code>005</code> was assigned to "software development", meaning <em>The C Programming Language</em> lives at <code>005.133</code>. </p> <p>Incidentally, another late addition to the general knowledge block is <code>001.9</code>: "controversial knowledge". </p> <p>And that's why my hometown library shelved the C++ books right next to <em>The Mothman Prophecies</em>.</p> <p>How's <em>that</em> for technical debt?</p> <p>If anything, fixing hacks in software is significantly <em>easier</em> than in other fields. This came up when I was <a href="https://www.hillelwayne.com/post/we-are-not-special/" target="_blank">interviewing classic engineers</a>. Kludges happened all the time, but "refactoring" them out is <em>expensive</em>. Need to house a machine that's just two inches taller than the room? Guess what, you're cutting a hole in the ceiling.</p> <p>(Even if we restrict the question to other departments in a <em>software company</em>, we can find kludges that are horrible to undo. I once worked for a company which landed an early contract by adding a bespoke support agreement for that one customer. That plagued them for years afterward.)</p> <p>That's not to say that there aren't things that are different about software vs other fields!<sup id="fnref:example"><a class="footnote-ref" href="#fn:example">2</a></sup> But I think that <em>most</em> of the time, when we say "software development is the only profession that deals with XYZ", it's only because we're ignorant of how those other professions work.</p> <hr/> <p>Short newsletter because I'm way behind on writing my <a href="https://www.aprilcools.club/" target="_blank">April Cools</a>. If you're interested in April Cools, you should try it out! I make it <em>way</em> harder on myself than it actually needs to be— everybody else who participates finds it pretty chill.</p> <div class="footnote"> <hr/> <ol> <li id="fn:ottinger"> <p>Ottinger caveats it with "engineering, software or otherwise", so I think he knows that other branches of <em>engineering</em>, at least, have kludges. <a class="footnote-backref" href="#fnref:ottinger" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:example"> <p>The "software is different" idea that I'm most sympathetic to is that in software, the tools we use and the products we create are made from the same material. That's unusual at least in classic engineering. Then again, plenty of machinists have made their own lathes and mills! <a class="footnote-backref" href="#fnref:example" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 26 Mar 2025 18:48:39 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/</guid></item><item><title>Verification-First Development</title><link>https://buttondown.com/hillelwayne/archive/verification-first-development/</link><description><p>A while back I argued on the Blue Site<sup id="fnref:li"><a class="footnote-ref" href="#fn:li">1</a></sup> that "test-first development" (TFD) was different than "test-driven development" (TDD). The former is "write tests before you write code", the latter is a paradigm, culture, and collection of norms that's based on TFD. More broadly, TFD is a special case of <strong>Verification-First Development</strong> and TDD is not.</p> <blockquote> <p>VFD: before writing code, put in place some means of verifying that the code is correct, or at least have an idea of what you'll do.</p> </blockquote> <p>"Verifying" could mean writing tests, or figuring out how to encode invariants in types, or <a href="https://blog.regehr.org/archives/1091" target="_blank">adding contracts</a>, or <a href="https://learntla.com/" target="_blank">making a formal model</a>, or writing a separate script that checks the output of the program. Just have <em>something</em> appropriate in place that you can run as you go building the code. Ideally, we'd have verification in place for every interesting property, but that's rarely possible in practice. </p> <p>Oftentimes we can't make the verification until the code is partially complete. In that case it still helps to figure out the verification we'll write later. The point is to have a <em>plan</em> and follow it promptly.</p> <p>I'm using "code" as a standin for anything we programmers make, not just software programs. When using constraint solvers, I try to find representative problems I know the answers to. When writing formal specifications, I figure out the system's properties before the design that satisfies those properties. There's probably equivalents in security and other topics, too.</p> <h3>The Benefits of VFD</h3> <ol> <li>Doing verification before coding makes it less likely we'll skip verification entirely. It's the professional equivalent of "No TV until you do your homework."</li> <li>It's easier to make sure a verifier works properly if we start by running it on code we know doesn't pass it. Bebugging working code takes more discipline.</li> <li>We can run checks earlier in the development process. It's better to realize that our code is broken five minutes after we broke it rather than two hours after.</li> </ol> <p>That's it, those are the benefits of verification-first development. Those are also <em>big</em> benefits for relatively little investment. Specializations of VFD like test-first development can have more benefits, but also more drawbacks.</p> <h3>The drawbacks of VFD</h3> <ol> <li>It slows us down. I know lots of people say that "no actually it makes you go faster in the long run," but that's the <em>long</em> run. Sometimes we do marathons, sometimes we sprint.</li> <li>Verification gets in the way of exploratory coding, where we don't know what exactly we want or how exactly to do something.</li> <li>Any specific form of verification exerts a pressure on our code to make it easier to verify with that method. For example, if we're mostly verifying via type invariants, we need to figure out how to express those things in our language's type system, which may not be suited for the specific invariants we need.<sup id="fnref:sphinx"><a class="footnote-ref" href="#fn:sphinx">2</a></sup></li> </ol> <h2>Whether "pressure" is a real drawback is incredibly controversial</h2> <p>If I had to summarize what makes "test-driven development" different from VFD:<sup id="fnref:tdd"><a class="footnote-ref" href="#fn:tdd">3</a></sup></p> <ol> <li>The form of verification should specifically be tests, and unit tests at that</li> <li>Testing pressure is invariably good. "Making your code easier to unit test" is the same as "making your code better".</li> </ol> <p>This is something all of the various "drivens"— TDD, Type Driven Development, Design by Contract— share in common, this idea that the purpose of the paradigm is to exert pressure. Lots of TDD experts claim that "having a good test suite" is only the secondary benefit of TDD and the real benefit is how it improves code quality.<sup id="fnref:docs"><a class="footnote-ref" href="#fn:docs">4</a></sup></p> <p>Whether they're right or not is not something I want to argue: I've seen these approaches all improve my code structure, but also sometimes worsen it. Regardless, I consider pressure a drawback to VFD in general, though, for a somewhat idiosyncratic reason. If it <em>weren't</em> for pressure, VFD would be wholly independent of the code itself. It would <em>just</em> be about verification, and our decisions would exclusively be about how we want to verify. But the design pressure means that our means of verification affects the system we're checking. What if these conflict in some way?</p> <h3>VFD is a technique, not a paradigm</h3> <p>One of the main differences between "techniques" and "paradigms" is that paradigms don't play well with each other. If you tried to do both "proper" Test-Driven Development and "proper" Cleanroom, your head would explode. Whereas VFD being a "technique" means it works well with other techniques and even with many full paradigms.</p> <p>It also doesn't take a whole lot of practice to start using. It does take practice, both in thinking of verifications and in using the particular verification method involved, to <em>use well</em>, but we can use it poorly and still benefit.</p> <div class="footnote"> <hr/> <ol> <li id="fn:li"> <p>LinkedIn, what did you think I meant? <a class="footnote-backref" href="#fnref:li" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:sphinx"> <p>This bit me in the butt when making my own <a href="https://www.sphinx-doc.org/en/master/" target="_blank">sphinx</a> extensions. The official guides do things in a highly dynamic way that Mypy can't statically check. I had to do things in a completely different way. Ended up being better though! <a class="footnote-backref" href="#fnref:sphinx" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:tdd"> <p>Someone's going to yell at me that I completely missed the point of TDD, which is XYZ. Well guess what, someone else <em>already</em> yelled at me that only dumb idiot babies think XYZ is important in TDD. Put in whatever you want for XYZ. <a class="footnote-backref" href="#fnref:tdd" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:docs"> <p>Another thing that weirdly all of the paradigms claim: that they lead to better documentation. I can see the argument, I just find it strange that <em>every single one</em> makes this claim! <a class="footnote-backref" href="#fnref:docs" title="Jump back to footnote 4 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 18 Mar 2025 16:22:20 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/verification-first-development/</guid></item><item><title>New Blog Post: "A Perplexing Javascript Parsing Puzzle"</title><link>https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/</link><description><p>I know I said we'd be back to normal newsletters this week and in fact had 80% of one already written. </p> <p>Then I unearthed something that was better left buried.</p> <p><a href="http://www.hillelwayne.com/post/javascript-puzzle/" target="_blank">Blog post here</a>, <a href="https://www.patreon.com/posts/blog-notes-124153641" target="_blank">Patreon notes here</a> (Mostly an explanation of how I found this horror in the first place). Next week I'll send what was supposed to be this week's piece.</p> <p>(PS: <a href="https://www.aprilcools.club/" target="_blank">April Cools</a> in three weeks!)</p></description><pubDate>Wed, 12 Mar 2025 14:49:52 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/</guid></item><item><title>Five Kinds of Nondeterminism</title><link>https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/</link><description><p>No newsletter next week, I'm teaching a TLA+ workshop.</p> <p>Speaking of which: I spend a lot of time thinking about formal methods (and TLA+ specifically) because it's where the source of almost all my revenue. But I don't share most of the details because 90% of my readers don't use FM and never will. I think it's more interesting to talk about ideas <em>from</em> FM that would be useful to people outside that field. For example, the idea of "property strength" translates to the <a href="https://buttondown.com/hillelwayne/archive/some-tests-are-stronger-than-others/" target="_blank">idea that some tests are stronger than others</a>. </p> <p>Another possible export is how FM approaches nondeterminism. A <strong>nondeterministic</strong> algorithm is one that, from the same starting conditions, has multiple possible outputs. This is nondeterministic:</p> <div class="codehilite"><pre><span></span><code># Pseudocode def f() { return rand()+1; } </code></pre></div> <p>When specifying systems, I may not <em>encounter</em> nondeterminism more often than in real systems, but I am definitely more aware of its presence. Modeling nondeterminism is a core part of formal specification. I mentally categorize nondeterminism into five buckets. Caveat, this is specifically about nondeterminism from the perspective of <em>system modeling</em>, not computer science as a whole. If I tried to include stuff on NFAs and amb operations this would be twice as long.<sup id="fnref:nondeterminism"><a class="footnote-ref" href="#fn:nondeterminism">1</a></sup></p> <p style="height:16px; margin:0px !important;"></p> <h2>1. True Randomness</h2> <p>Programs that literally make calls to a <code>random</code> function and then use the results. This the simplest type of nondeterminism and one of the most ubiquitous. </p> <p>Most of the time, <code>random</code> isn't <em>truly</em> nondeterministic. Most of the time computer randomness is actually <strong>pseudorandom</strong>, meaning we seed a deterministic algorithm that behaves "randomly-enough" for some use. You could "lift" a nondeterministic random function into a deterministic one by adding a fixed seed to the starting state.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Python</span> <span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span><span class="p">,</span> <span class="n">seed</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="n">seed</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">return</span> <span class="n">random</span><span class="p">()</span> <span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="mf">0.23796462709189137</span> <span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="mf">0.23796462709189137</span> </code></pre></div> <p>Often we don't do this because the <em>point</em> of randomness is to provide nondeterminism! We deliberately <em>abstract out</em> the starting state of the seed from our program, because it's easier to think about it as locally nondeterministic.</p> <p>(There's also "true" randomness, like using <a href="https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html#inpage-nav-3-2" target="_blank">thermal noise</a> as an entropy source, which I think are mainly used for cryptography and seeding PRNGs.)</p> <p>Most formal specification languages don't deal with randomness (though some deal with <a href="https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/" target="_blank">probability more broadly</a>). Instead, we treat it as a nondeterministic choice:</p> <div class="codehilite"><pre><span></span><code># software if rand > 0.001 then return a else crash # specification either return a or crash </code></pre></div> <p>This is because we're looking at worst-case scenarios, so it doesn't matter if <code>crash</code> happens 50% of the time or 0.0001% of the time, it's still possible. </p> <h2>2. Concurrency</h2> <div class="codehilite"><pre><span></span><code># Pseudocode global x = 1, y = 0; def thread1() { x++; x++; x++; } def thread2() { y := x; } </code></pre></div> <p>If <code>thread1()</code> and <code>thread2()</code> run sequentially, then (assuming the sequence is fixed) the final value of <code>y</code> is deterministic. If the two functions are started and run simultaneously, then depending on when <code>thread2</code> executes <code>y</code> can be 1, 2, 3, <em>or</em> 4. Both functions are locally sequential, but running them concurrently leads to global nondeterminism.</p> <p>Concurrency is arguably the most <em>dramatic</em> source of nondeterminism. <a href="https://buttondown.com/hillelwayne/archive/what-makes-concurrency-so-hard/" target="_blank">Small amounts of concurrency lead to huge explosions in the state space</a>. We have words for the specific kinds of nondeterminism caused by concurrency, like "race condition" and "dirty write". Often we think about it as a separate <em>topic</em> from nondeterminism. To some extent it "overshadows" the other kinds: I have a much easier time teaching students about concurrency in models than nondeterminism in models.</p> <p>Many formal specification languages have special syntax/machinery for the concurrent aspects of a system, and generic syntax for other kinds of nondeterminism. In P that's <a href="https://p-org.github.io/P/manual/expressions/#choose" target="_blank">choose</a>. Others don't special-case concurrency, instead representing as it as nondeterministic choices by a global coordinator. This more flexible but also more inconvenient, as you have to implement process-local sequencing code yourself. </p> <h2>3. User Input</h2> <div class="subscribe-form"></div> <p>One of the most famous and influential programming books is <em>The C Programming Language</em> by Kernighan and Ritchie. The first example of a nondeterministic program appears on page 14:</p> <p><img alt="Picture of the book page. Code reproduced below." class="newsletter-image" src="https://assets.buttondown.email/images/94e6ad15-8d09-48df-b885-191318bfd179.jpg?w=960&fit=max"/></p> <p>For the newsletter readers who get text only emails,<sup id="fnref:text-only"><a class="footnote-ref" href="#fn:text-only">2</a></sup> here's the program:</p> <div class="codehilite"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf"><stdio.h></span> <span class="cm">/* copy input to output; 1st version */</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">c</span><span class="p">;</span> <span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getchar</span><span class="p">();</span> <span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">c</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">EOF</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">putchar</span><span class="p">(</span><span class="n">c</span><span class="p">);</span> <span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getchar</span><span class="p">();</span> <span class="w"> </span><span class="p">}</span> <span class="p">}</span> </code></pre></div> <p>Yup, that's nondeterministic. Because the user can enter any string, any call of <code>main()</code> could have any output, meaning the number of possible outcomes is infinity.</p> <p>Okay that seems a little cheap, and I think it's because we tend to think of determinism in terms of how the user <em>experiences</em> the program. Yes, <code>main()</code> has an infinite number of user inputs, but for each input the user will experience only one possible output. It starts to feel more nondeterministic when modeling a long-standing system that's <em>reacting</em> to user input, for example a server that runs a script whenever the user uploads a file. This can be modeled with nondeterminism and concurrency: We have one execution that's the system, and one nondeterministic execution that represents the effects of our user.</p> <p>(One intrusive thought I sometimes have: any "yes/no" dialogue actually has <em>three</em> outcomes: yes, no, or the user getting up and walking away without picking a choice, permanently stalling the execution.)</p> <h2>4. External forces</h2> <p>The more general version of "user input": anything where either 1) some part of the execution outcome depends on retrieving external information, or 2) the external world can change some state outside of your system. I call the distinction between internal and external components of the system <a href="https://www.hillelwayne.com/post/world-vs-machine/" target="_blank">the world and the machine</a>. Simple examples: code that at some point reads an external temperature sensor. Unrelated code running on a system which quits programs if it gets too hot. API requests to a third party vendor. Code processing files but users can delete files before the script gets to them.</p> <p>Like with PRNGs, some of these cases don't <em>have</em> to be nondeterministic; we can argue that "the temperature" should be a virtual input into the function. Like with PRNGs, we treat it as nondeterministic because it's useful to think in that way. Also, what if the temperature changes between starting a function and reading it?</p> <p>External forces are also a source of nondeterminism as <em>uncertainty</em>. Measurements in the real world often comes with errors, so repeating a measurement twice can give two different answers. Sometimes operations fail for no discernable reason, or for a non-programmatic reason (like something physically blocks the sensor).</p> <p>All of these situations can be modeled in the same way as user input: a concurrent execution making nondeterministic choices.</p> <h2>5. Abstraction</h2> <p>This is where nondeterminism in system models and in "real software" differ the most. I said earlier that pseudorandomness is <em>arguably</em> deterministic, but we abstract it into nondeterminism. More generally, <strong>nondeterminism hides implementation details of deterministic processes</strong>.</p> <p>In one consulting project, we had a machine that received a message, parsed a lot of data from the message, went into a complicated workflow, and then entered one of three states. The final state was totally deterministic on the content of the message, but the actual process of determining that final state took tons and tons of code. None of that mattered at the scope we were modeling, so we abstracted it all away: "on receiving message, nondeterministically enter state A, B, or C."</p> <p>Doing this makes the system easier to model. It also makes the model more sensitive to possible errors. What if the workflow is bugged and sends us to the wrong state? That's already covered by the nondeterministic choice! Nondeterministic abstraction gives us the potential to pick the worst-case scenario for our system, so we can prove it's robust even under those conditions.</p> <p>I know I beat the "nondeterminism as abstraction" drum a whole lot but that's because it's the insight from formal methods I personally value the most, that nondeterminism is a powerful tool to <em>simplify reasoning about things</em>. You can see the same approach in how I approach modeling users and external forces: complex realities black-boxed and simplified into nondeterministic forces on the system.</p> <hr/> <p>Anyway, I hope this collection of ideas I got from formal methods are useful to my broader readership. Lemme know if it somehow helps you out!</p> <div class="footnote"> <hr/> <ol> <li id="fn:nondeterminism"> <p>I realized after writing this that I already talked wrote an essay about nondeterminism in formal specification <a href="https://buttondown.com/hillelwayne/archive/nondeterminism-in-formal-specification/" target="_blank">just under a year ago</a>. I hope this one covers enough new ground to be interesting! <a class="footnote-backref" href="#fnref:nondeterminism" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:text-only"> <p>There is a surprising number of you. <a class="footnote-backref" href="#fnref:text-only" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 19 Feb 2025 19:37:57 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/</guid></item><item><title>Are Efficiency and Horizontal Scalability at odds?</title><link>https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/</link><description><p>Sorry for missing the newsletter last week! I started writing on Monday as normal, and by Wednesday the piece (about the <a href="https://en.wikipedia.org/wiki/Hierarchy_of_hazard_controls" target="_blank">hierarchy of controls</a> ) was 2000 words and not <em>close</em> to done. So now it'll be a blog post sometime later this month.</p> <p>I also just released a new version of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>! 0.7 adds a bunch of new content (type invariants, modeling access policies, rewrites of the first chapters) but more importantly has new fonts that are more legible than the old ones. <a href="https://leanpub.com/logic/" target="_blank">Go check it out!</a></p> <p>For this week's newsletter I want to brainstorm an idea I've been noodling over for a while. Say we have a computational task, like running a simulation or searching a very large graph, and it's taking too long to complete on a computer. There's generally three things that we can do to make it faster:</p> <ol> <li>Buy a faster computer ("vertical scaling")</li> <li>Modify the software to use the computer's resources better ("efficiency")</li> <li>Modify the software to use multiple computers ("horizontal scaling")</li> </ol> <p>(Splitting single-threaded software across multiple threads/processes is sort of a blend of (2) and (3).)</p> <p>The big benefit of (1) is that we (usually) don't have to make any changes to the software to get a speedup. The downside is that for the past couple of decades computers haven't <em>gotten</em> much faster, except in ways that require recoding (like GPUs and multicore). This means we rely on (2) and (3), and we can do both to a point. I've noticed, though, that horizontal scaling seems to conflict with efficiency. Software optimized to scale well tends to be worse or the <code>N=1</code> case than software optimized to, um, be optimized. </p> <p>Are there reasons to <em>expect</em> this? It seems reasonable that design goals of software are generally in conflict, purely because exclusively optimizing for one property means making decisions that impede other properties. But is there something in the nature of "efficiency" and "horizontal scalability" that make them especially disjoint?</p> <p>This isn't me trying to explain a fully coherent idea, more me trying to figure this all out to myself. Also I'm probably getting some hardware stuff wrong</p> <h3>Amdahl's Law</h3> <p>According to <a href="https://en.wikipedia.org/wiki/Amdahl%27s_law" target="_blank">Amdahl's Law</a>, the maximum speedup by parallelization is constrained by the proportion of the work that can be parallelized. If 80% of algorithm X is parallelizable, the maximum speedup from horizontal scaling is 5x. If algorithm Y is 25% parallelizable, the maximum speedup is only 1.3x. </p> <p>If you need horizontal scalability, you want to use algorithm X, <em>even if Y is naturally 3x faster</em>. But if Y was 4x faster, you'd prefer it to X. Maximal scalability means finding the optimal balance between baseline speed and parallelizability. Maximal efficiency means just optimizing baseline speed. </p> <h3>Coordination Overhead</h3> <p>Distributed algorithms require more coordination. To add a list of numbers in parallel via <a href="https://en.wikipedia.org/wiki/Fork%E2%80%93join_model" target="_blank">fork-join</a>, we'd do something like this:</p> <ol> <li>Split the list into N sublists</li> <li>Fork a new thread/process for sublist</li> <li>Wait for each thread/process to finish</li> <li>Add the sums together.</li> </ol> <p>(1), (2), and (3) all add overhead to the algorithm. At the very least, it's extra lines of code to execute, but it can also mean inter-process communication or network hops. Distribution also means you have fewer natural correctness guarantees, so you need more administrative overhead to avoid race conditions. </p> <p><strong>Real world example:</strong> Historically CPython has a "global interpreter lock" (GIL). In multithreaded code, only one thread could execute Python code at a time (others could execute C code). The <a href="https://docs.python.org/3/howto/free-threading-python.html#single-threaded-performance" target="_blank">newest version</a> supports disabling the GIL, which comes at a 40% overhead for single-threaded programs. Supposedly the difference is because the <a href="https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep659" target="_blank">specializing adaptor</a> optimization isn't thread-safe yet. The Python team is hoping on getting it down to "only" 10%. </p> <p style="height:16px; margin:0px !important;"></p> <h3>Scaling loses shared resources</h3> <p>I'd say that intra-machine scaling (multiple threads/processes) feels qualitatively <em>different</em> than inter-machine scaling. Part of that is that intra-machine scaling is "capped" while inter-machine is not. But there's also a difference in what assumptions you can make about shared resources. Starting from the baseline of single-threaded program:</p> <ol> <li>Threads have a much harder time sharing CPU caches (you have to manually mess with affinities)</li> <li>Processes have a much harder time sharing RAM (I think you have to use <a href="https://en.wikipedia.org/wiki/Memory-mapped_file" target="_blank">mmap</a>?)</li> <li>Machines can't share cache, RAM, or disk, period.</li> </ol> <p>It's a lot easier to solve a problem when the whole thing fits in RAM. But if you split a 50 gb problem across three machines, it doesn't fit in ram by default, even if the machines have 64 gb each. Scaling also means that separate machines can't reuse resources like database connections.</p> <h3>Efficiency comes from limits</h3> <p>I think the two previous points tie together in the idea that maximal efficiency comes from being able to make assumptions about the system. If we know the <em>exact</em> sequence of computations, we can aim to minimize cache misses. If we don't have to worry about thread-safety, <a href="https://www.playingwithpointers.com/blog/refcounting-harder-than-it-sounds.html" target="_blank">tracking references is dramatically simpler</a>. If we have all of the data in a single database, our query planner has more room to work with. At various tiers of scaling these assumptions are no longer guaranteed and we lose the corresponding optimizations.</p> <p>Sometimes these assumptions are implicit and crop up in odd places. Like if you're working at a scale where you need multiple synced databases, you might want to use UUIDs instead of numbers for keys. But then you lose the assumption "recently inserted rows are close together in the index", which I've read <a href="https://www.cybertec-postgresql.com/en/unexpected-downsides-of-uuid-keys-in-postgresql/" target="_blank">can lead to significant slowdowns</a>. </p> <p>This suggests that if you can find a limit somewhere else, you can get both high horizontal scaling and high efficiency. <del>Supposedly the <a href="https://tigerbeetle.com/" target="_blank">TigerBeetle database</a> has both, but that could be because they limit all records to <a href="https://docs.tigerbeetle.com/coding/" target="_blank">accounts and transfers</a>. This means every record fits in <a href="https://tigerbeetle.com/blog/2024-07-23-rediscovering-transaction-processing-from-history-and-first-principles/#transaction-processing-from-first-principles" target="_blank">exactly 128 bytes</a>.</del> [A TigerBeetle engineer reached out to tell me that they do <em>not</em> horizontally scale compute, they distribute across multiple nodes for redundancy. <a href="https://lobste.rs/s/5akiq3/are_efficiency_horizontal_scalability#c_ve8ud5" target="_blank">"You can't make it faster by adding more machines."</a>]</p> <p>Does this mean that "assumptions" could be both "assumptions about the computing environment" and "assumptions about the problem"? In the famous essay <a href="http://www.frankmcsherry.org/graph/scalability/cost/2015/01/15/COST.html" target="_blank">Scalability! But at what COST</a>, Frank McSherry shows that his single-threaded laptop could outperform 128-node "big data systems" on PageRank and graph connectivity (via label propagation). Afterwards, he discusses how a different algorithm solves graph connectivity even faster: </p> <blockquote> <p>[Union find] is more line of code than label propagation, but it is 10x faster and 100x less embarassing. … The union-find algorithm is fundamentally incompatible with the graph computation approaches Giraph, GraphLab, and GraphX put forward (the so-called “think like a vertex” model).</p> </blockquote> <p>The interesting thing to me is that his alternate makes more "assumptions" than what he's comparing to. He can "assume" a fixed goal and optimize the code for that goal. The "big data systems" are trying to be general purpose compute platforms and have to pick a model that supports the widest range of possible problems. </p> <p>A few years back I wrote <a href="https://www.hillelwayne.com/post/cleverness/" target="_blank">clever vs insightful code</a>, I think what I'm trying to say here is that efficiency comes from having insight into your problem and environment.</p> <p>(Last thought to shove in here: to exploit assumptions, you need <em>control</em>. Carefully arranging your data to fit in L1 doesn't matter if your programming language doesn't let you control where things are stored!)</p> <h3>Is there a cultural aspect?</h3> <p>Maybe there's also a cultural element to this conflict. What if the engineers interested in "efficiency" are different from the engineers interested in "horizontal scaling"?</p> <p>At my first job the data scientists set up a <a href="https://en.wikipedia.org/wiki/Apache_Hadoop" target="_blank">Hadoop</a> cluster for their relatively small dataset, only a few dozen gigabytes or so. One of the senior software engineers saw this and said "big data is stupid." To prove it, he took one of their example queries, wrote a script in Go to compute the same thing, and optimized it to run faster on his machine.</p> <p>At the time I was like "yeah, you're right, big data IS stupid!" But I think now that we both missed something obvious: with the "scalable" solution, the data scientists <em>didn't</em> have to write an optimized script for every single query. Optimizing code is hard, adding more machines is easy! </p> <p>The highest-tier of horizontal scaling is usually something large businesses want, and large businesses like problems that can be solved purely with money. Maximizing efficiency requires a lot of knowledge-intensive human labour, so is less appealing as an investment. Then again, I've seen a lot of work on making the scalable systems more efficient, such as evenly balancing heterogeneous workloads. Maybe in the largest systems intra-machine efficiency is just too small-scale a problem. </p> <h3>I'm not sure where this fits in but scaling a volume of tasks conflicts less than scaling individual tasks</h3> <p>If you have 1,000 machines and need to crunch one big graph, you probably want the most scalable algorithm. If you instead have 50,000 small graphs, you probably want the most efficient algorithm, which you then run on all 1,000 machines. When we call a problem <a href="https://en.wikipedia.org/wiki/Embarrassingly_parallel" target="_blank">embarrassingly parallel</a>, we usually mean it's easy to horizontally scale. But it's also one that's easy to make more efficient, because local optimizations don't affect the scaling! </p> <hr/> <p>Okay that's enough brainstorming for one week.</p> <h3>Blog Rec</h3> <p>Whenever I think about optimization as a skill, the first article that comes to mind is <a href="https://matklad.github.io/" target="_blank">Mat Klad's</a> <a href="https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html" target="_blank">Push Ifs Up And Fors Down</a>. I'd never have considered on my own that inlining loops into functions could be such a huge performance win. The blog has a lot of other posts on the nuts-and-bolts of systems languages, optimization, and concurrency.</p></description><pubDate>Wed, 12 Feb 2025 18:26:20 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/</guid></item><item><title>What hard thing does your tech make easy?</title><link>https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/</link><description><p>I occasionally receive emails asking me to look at the writer's new language/library/tool. Sometimes it's in an area I know well, like formal methods. Other times, I'm a complete stranger to the field. Regardless, I'm generally happy to check it out.</p> <p>When starting out, this is the biggest question I'm looking to answer:</p> <blockquote> <p>What does this technology make easy that's normally hard?</p> </blockquote> <p>What justifies me learning and migrating to a <em>new</em> thing as opposed to fighting through my problems with the tools I already know? The new thing has to have some sort of value proposition, which could be something like "better performance" or "more secure". The most universal value and the most direct to show is "takes less time and mental effort to do something". I can't accurately judge two benchmarks, but I can see two demos or code samples and compare which one feels easier to me.</p> <h2>Examples</h2> <h3>Functional programming</h3> <p>What drew me originally to functional programming was higher order functions. </p> <div class="codehilite"><pre><span></span><code># Without HOFs out = [] for x in input { if test(x) { out.append(x) } } # With HOFs filter(test, input) </code></pre></div> <p style="height:16px; margin:0px !important;"></p> <p>We can also compare the easiness of various tasks between examples within the same paradigm. If I know FP via Clojure, what could be appealing about Haskell or F#? For one, null safety is a lot easier when I've got option types.</p> <h3>Array Programming</h3> <p>Array programming languages like APL or J make certain classes of computation easier. For example, finding all of the indices where two arrays <del>differ</del> match. Here it is in Python:</p> <div class="codehilite"><pre><span></span><code><span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="n">y</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="o">>>></span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span> <span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">]</span> <span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span> </code></pre></div> <p>And here it is in J:</p> <div class="codehilite"><pre><span></span><code><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">4</span> <span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">4</span> <span class="w"> </span><span class="nv">I</span><span class="o">.</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">y</span> <span class="mi">7</span><span class="w"> </span><span class="mi">9</span> </code></pre></div> <p>Not every tool is meant for every programmer, because you might not have any of the problems a tool makes easier. What comes up more often for you: filtering a list or finding all the indices where two lists differ? Statistically speaking, functional programming is more useful to you than array programming.</p> <p>But <em>I</em> have this problem enough to justify learning array programming.</p> <h3>LLMs</h3> <p>I think a lot of the appeal of LLMs is they make a lot of specialist tasks easy for nonspecialists. One thing I recently did was convert some rst <a href="https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table" target="_blank">list tables</a> to <a href="https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1" target="_blank">csv tables</a>. Normally I'd have to do write some tricky parsing and serialization code to automatically convert between the two. With LLMs, it's just</p> <blockquote> <p>Convert the following rst list-table into a csv-table: [table]</p> </blockquote> <p>"Easy" can trump "correct" as a value. The LLM might get some translations wrong, but it's so convenient I'd rather manually review all the translations for errors than write specialized script that is correct 100% of the time.</p> <h2>Let's not take this too far</h2> <p>A college friend once claimed that he cracked the secret of human behavior: humans do whatever makes them happiest. "What about the martyr who dies for their beliefs?" "Well, in their last second of life they get REALLY happy."</p> <p>We can do the same here, fitting every value proposition into the frame of "easy". CUDA makes it easier to do matrix multiplication. Rust makes it easier to write low-level code without memory bugs. TLA+ makes it easier to find errors in your design. Monads make it easier to sequence computations in a lazy environment. Making everything about "easy" obscures other reason for adopting new things.</p> <h3>That whole "simple vs easy" thing</h3> <p>Sometimes people think that "simple" is better than "easy", because "simple" is objective and "easy" is subjective. This comes from the famous talk <a href="https://www.infoq.com/presentations/Simple-Made-Easy/" target="_blank">Simple Made Easy</a>. I'm not sure I agree that simple is better <em>or</em> more objective: the speaker claims that polymorphism and typeclasses are "simpler" than conditionals, and I doubt everybody would agree with that.</p> <p>The problem is that "simple" is used to mean both "not complicated" <em>and</em> "not complex". And everybody agrees that "complicated" and "complex" are different, even if they can't agree <em>what</em> the difference is. This idea should probably expanded be expanded into its own newsletter.</p> <p>It's also a lot harder to pitch a technology on being "simpler". Simplicity by itself doesn't make a tool better equipped to solve problems. Simplicity can unlock other benefits, like compositionality or <a href="https://buttondown.com/hillelwayne/archive/the-capability-tractability-tradeoff/" target="_blank">tractability</a>, that provide the actual value. And often that value is in the form of "makes some tasks easier". </p></description><pubDate>Wed, 29 Jan 2025 18:09:47 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/</guid></item><item><title>The Juggler's Curse</title><link>https://buttondown.com/hillelwayne/archive/the-jugglers-curse/</link><description><p>I'm making a more focused effort to juggle this year. Mostly <a href="https://youtu.be/PPhG_90VH5k?si=AxOO65PcX4ZwnxPQ&t=49" target="_blank">boxes</a>, but also classic balls too.<sup id="fnref:boxes"><a class="footnote-ref" href="#fn:boxes">1</a></sup> I've gotten to the point where I can almost consistently do a five-ball cascade, which I <em>thought</em> was the cutoff to being a "good juggler". "Thought" because I now know a "good juggler" is one who can do the five-ball cascade with <em>outside throws</em>. </p> <p>I know this because I can't do the outside five-ball cascade... yet. But it's something I can see myself eventually mastering, unlike the slightly more difficult trick of the five-ball mess, which is impossible for mere mortals like me. </p> <p><em>In theory</em> there is a spectrum of trick difficulties and skill levels. I could place myself on the axis like this:</p> <p><img alt="A crudely-drawn scale with 10 even ticks, I'm between 5 and 6" class="newsletter-image" src="https://assets.buttondown.email/images/8ee51aa1-5dd4-48b8-8110-2cdf9a273612.png?w=960&fit=max"/></p> <p>In practice, there are three tiers:</p> <ol> <li>Toddlers</li> <li>Good jugglers who practice hard</li> <li>Genetic freaks and actual wizards</li> </ol> <p>And the graph always, <em>always</em> looks like this:</p> <p><img alt="The same graph, with the top compressed into "wizards" and bottom into "toddlers". I'm in toddlers." class="newsletter-image" src="https://assets.buttondown.email/images/04c76cec-671e-4560-b64e-498b7652359e.png?w=960&fit=max"/></p> <p>This is the jugglers curse, and it's a three-parter:</p> <ol> <li>The threshold between you and "good" is the next trick you cannot do.</li> <li>Everything below that level is trivial. Once you've gotten a trick down, you can never go back to not knowing it, to appreciating how difficult it was to learn in the first place.<sup id="fnref:expert-blindness"><a class="footnote-ref" href="#fn:expert-blindness">2</a></sup></li> <li>Everything above that level is just "impossible". You don't have the knowledge needed to recognize the different tiers.<sup id="fnref:dk"><a class="footnote-ref" href="#fn:dk">3</a></sup></li> </ol> <p>So as you get better, the stuff that was impossible becomes differentiable, and you can see that some of it <em>is</em> possible. And everything you learned becomes trivial. So you're never a good juggler until you learn "just one more hard trick".</p> <p>The more you know, the more you know you don't know and the less you know you know.</p> <h3>This is supposed to be a software newsletter</h3> <blockquote> <p>A monad is a monoid in the category of endofunctors, what's the problem? <a href="https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html" target="_blank">(src)</a></p> </blockquote> <p>I think this applies to any difficult topic? Most fields don't have the same stark <a href="https://en.wikipedia.org/wiki/Spectral_line" target="_blank">spectral lines</a> as juggling, but there's still tiers of difficulty to techniques, which get compressed the further in either direction they are from your current level.</p> <p>Like, I'm not good at formal methods. I've written two books on it but I've never mastered a dependently-typed language or a theorem prover. Those are equally hard. And I'm not good at modeling concurrent systems because I don't understand the formal definition of bisimulation and haven't implemented a Raft. Those are also equally hard, in fact exactly as hard as mastering a theorem prover.</p> <p>At the same time, the skills I've already developed are easy: properly using refinement is <em>exactly as easy</em> as writing <a href="https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/" target="_blank">a wrapped counter</a>. Then I get surprised when I try to explain strong fairness to someone and they just don't get how □◇(ENABLED〈A〉ᵥ) is <em>obviously</em> different from ◇□(ENABLED 〈A〉ᵥ).</p> <p>Juggler's curse!</p> <p>Now I don't actually know if this is actually how everybody experiences expertise or if it's just my particular personality— I was a juggler long before I was a software developer. Then again, I'd argue that lots of people talk about one consequence of the juggler's curse: imposter syndrome. If you constantly think what you know is "trivial" and what you don't know is "impossible", then yeah, you'd start feeling like an imposter at work real quick.</p> <p>I wonder if part of the cause is that a lot of skills you have to learn are invisible. One of my favorite blog posts ever is <a href="https://www.benkuhn.net/blub/" target="_blank">In Defense of Blub Studies</a>, which argues that software expertise comes through understanding "boring" topics like "what all of the error messages mean" and "how to use a debugger well". Blub is a critical part of expertise and takes a lot of hard work to learn, but it <em>feels</em> like trivia. So looking back on a skill I mastered, I might think it was "easy" because I'm not including all of the blub that I had to learn, too.</p> <p>The takeaway, of course, is that the outside five-ball cascade <em>is</em> objectively the cutoff between good jugglers and toddlers.</p> <div class="footnote"> <hr/> <ol> <li id="fn:boxes"> <p>Rant time: I <em>love</em> cigar box juggling. It's fun, it's creative, it's totally unlike any other kind of juggling. And it's so niche I straight up cannot find anybody in Chicago to practice with. I once went to a juggling convention and was the only person with a cigar box set there. <a class="footnote-backref" href="#fnref:boxes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:expert-blindness"> <p>This particular part of the juggler's curse is also called <a href="https://en.wikipedia.org/wiki/Curse_of_knowledge" target="_blank">the curse of knowledge</a> or "expert blindness". <a class="footnote-backref" href="#fnref:expert-blindness" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:dk"> <p>This isn't Dunning-Kruger, because DK says that people think they are <em>better</em> than they actually are, and also <a href="https://www.mcgill.ca/oss/article/critical-thinking/dunning-kruger-effect-probably-not-real" target="_blank">may not actually be real</a>. <a class="footnote-backref" href="#fnref:dk" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 22 Jan 2025 18:50:40 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/the-jugglers-curse/</guid></item><item><title>What are the Rosettas of formal specification?</title><link>https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/</link><description><p>First of all, I just released version 0.6 of <em>Logic for Programmers</em>! You can get it <a href="https://leanpub.com/logic/" target="_blank">here</a>. Release notes in the footnote.<sup id="fnref:release-notes"><a class="footnote-ref" href="#fn:release-notes">1</a></sup></p> <p>I've been thinking about my next project after the book's done. One idea is to do a survey of new formal specification languages. There's been a lot of new ones in the past few years (P, Quint, etc), plus some old ones I haven't critically examined (SPIN, mcrl2). I'm thinking of a brief overview of each, what's interesting about it, and some examples of the corresponding models.</p> <p>For this I'd want a set of "Rosetta" examples. <a href="https://rosettacode.org/wiki/Rosetta_Code" target="_blank">Rosetta Code</a> is a collection of programming tasks done in different languages. For example, <a href="https://rosettacode.org/wiki/99_bottles_of_beer" target="_blank">"99 bottles of beer on the wall"</a> in over 300 languages. If I wanted to make a Rosetta Code for specifications of concurrent systems, what examples would I use? </p> <h3>What makes a good Rosetta examples?</h3> <p>A good Rosetta example would be simple enough to understand and implement but also showcase the differences between the languages. </p> <p>A good example of a Rosetta example is <a href="https://github.com/hwayne/lets-prove-leftpad" target="_blank">leftpad for code verification</a>. Proving leftpad correct is short in whatever verification language you use. But the proofs themselves are different enough that you can compare what it's like to use code contracts vs with dependent types, etc. </p> <p>A <em>bad</em> Rosetta example is "hello world". While it's good for showing how to run a language, it doesn't clearly differentiate languages. Haskell's "hello world" is almost identical to BASIC's "hello world".</p> <p>Rosetta examples don't have to be flashy, but I <em>want</em> mine to be flashy. Formal specification is niche enough that regardless of my medium, most of my audience hasn't use it and may be skeptical. I always have to be selling. This biases me away from using things like dining philosophers or two-phase commit.</p> <p>So with that in mind, three ideas:</p> <h3>1. Wrapped Counter</h3> <p>A counter that starts at 1 and counts to N, after which it wraps around to 1 again.</p> <h4>Why it's good</h4> <p>This is a good introductory formal specification: it's a minimal possible stateful system without concurrency or nondeterminism. You can use it to talk about the basic structure of a spec, how a verifier works, etc. It also a good way of introducing "boring" semantics, like conditionals and arithmetic, and checking if the language does anything unusual with them. Alloy, for example, defaults to 4-bit signed integers, so you run into problems if you set N too high.<sup id="fnref:alloy"><a class="footnote-ref" href="#fn:alloy">2</a></sup></p> <p>At the same time, wrapped counters are a common building block of complex systems. Lots of things can be represented this way: <code>N=1</code> is a flag or blinker, <code>N=3</code> is a traffic light, <code>N=24</code> is a clock, etc.</p> <p>The next example is better for showing basic <a href="https://www.hillelwayne.com/post/safety-and-liveness/" target="_blank">safety and liveness properties</a>, but this will do in a pinch. </p> <h3>2. Threads</h3> <p>A counter starts at 0. N threads each, simultaneously try to update the counter. They do this nonatomically: first they read the value of the counter and store that in a thread-local <code>tmp</code>, then they increment <code>tmp</code>, then they set the counter to <code>tmp</code>. The expected behavior is that the final value of the counter will be N.</p> <h4>Why it's good</h4> <p>The system as described is bugged. If two threads interleave the setlocal commands, one thread update can "clobber" the other and the counter can go backwards. To my surprise, most people <em>do not</em> see this error. So it's a good showcase of how the language actually finds real bugs, and how it can verify fixes.</p> <p>As to actual language topics: the spec covers concurrency and track process-local state. A good spec language should make it possible to adjust N without having to add any new variables. And it "naturally" introduces safety, liveness, and <a href="https://www.hillelwayne.com/post/action-properties/" target="_blank">action</a> properties.</p> <p>Finally, the thread spec is endlessly adaptable. I've used variations of it to teach refinement, resource starvation, fairness, livelocks, and hyperproperties. Tweak it a bit and you get dining philosophers.</p> <h3>3. Bounded buffer</h3> <p>We have a bounded buffer with maximum length <code>X</code>. We have <code>R</code> reader and <code>W</code> writer processes. Before writing, writers first check if the buffer is full. If full, the writer goes to sleep. Otherwise, the writer wakes up <em>a random</em> sleeping process, then pushes an arbitrary value. Readers work the same way, except they pop from the buffer (and go to sleep if the buffer is empty).</p> <p>The only way for a sleeping process to wake up is if another process successfully performs a read or write.</p> <h4>Why it's good</h4> <p>This shows process-local nondeterminism (in choosing which sleeping process to wake up), different behavior for different types of processes, and deadlocks: it's possible for every reader and writer to be asleep at the same time.</p> <p>The beautiful thing about this example: the spec can only deadlock if <code>X < 2*(R+W)</code>. This is the kind of bug you'd struggle to debug in real code. An in fact, people did struggle: even when presented with a minimal code sample and told there was a bug, many <a href="http://wiki.c2.com/?ExtremeProgrammingChallengeFourteen" target="_blank">testing experts couldn't find it</a>. Whereas a formal model of the same code <a href="https://www.hillelwayne.com/post/augmenting-agile/" target="_blank">finds the bug in seconds</a>. </p> <p>If a spec language can model the bounded buffer, then it's good enough for production systems.</p> <p>On top of that, the bug happens regardless of what writers actually put in the buffer, so you can abstract that all away. This example can demonstrate that you can leave implementation details out of a spec and still find critical errors.</p> <h2>Caveat</h2> <p>This is all with a <em>heavy</em> TLA+ bias. I've modeled all of these systems in TLA+ and it works pretty well for them. That is to say, none of these do things TLA+ is <em>bad</em> at: reachability, subtyping, transitive closures, unbound spaces, etc. I imagine that as I cover more specification languages I'll find new Rosettas.</p> <div class="footnote"> <hr/> <ol> <li id="fn:release-notes"> <ul> <li>Exercises are more compact, answers now show name of exercise in title</li> </ul> <ul> <li>"Conditionals" chapter has new section on nested conditionals</li> </ul> <ul> <li>"Crash course" chapter significantly rewritten</li> <li>Starting migrating to use consistently use <code>==</code> for equality and <code>=</code> for definition. Not everything is migrated yet</li> <li>"Beyond Logic" appendix does a <em>slightly</em> better job of covering HOL and constructive logic</li> <li>Addressed various reader feedback</li> <li>Two new exercises</li> </ul> <p><a class="footnote-backref" href="#fnref:release-notes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:alloy"> <p>You can change the int size in a model run, so this is more "surprising footgun and inconvenience" than "fundamental limit of the specification language." Something still good to know! <a class="footnote-backref" href="#fnref:alloy" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 15 Jan 2025 17:34:40 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/</guid></item><item><title>"Logic for Programmers" Project Update</title><link>https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/</link><description><p>Happy new year everyone!</p> <p>I released the first <em>Logic for Programmers</em> alpha six months ago. There's since been four new versions since then, with the November release putting us in beta. Between work and holidays I didn't make much progress in December, but there will be a 0.6 release in the next week or two.</p> <p>People have asked me if the book will ever be available in print, and my answer to that is "when it's done". To keep "when it's done" from being "never", I'm committing myself to <strong>have the book finished by July.</strong> That means roughly six more releases between now and the official First Edition. Then I will start looking for a way to get it printed.</p> <h3>The Current State and What Needs to be Done</h3> <p>Right now the book is 26,000 words. For the most part, the structure is set— I don't plan to reorganize the chapters much. But I still need to fix shortcomings identified by the reader feedback. In particular, a few topics need more on real world applications, and the Alloy chapter is pretty weak. There's also a bunch of notes and todos and "fix this"s I need to go over.</p> <p>I also need to rewrite the introduction and predicate logic chapters. Those haven't changed much since 0.1 and I need to go over them <em>very carefully</em>.</p> <p>After that comes copyediting.</p> <h4>Ugh, Copyediting</h4> <p>Copyediting means going through the entire book to make word and sentence sentence level changes to the flow. An example would be changing</p> <table> <thead> <tr> <th>From</th> <th>To</th> </tr> </thead> <tbody> <tr> <td>I said predicates are just “boolean functions”. That isn’t <em>quite</em> true.</td> <td>It's easy to think of predicates as just "boolean" functions, but there is a subtle and important difference.</td> </tr> </tbody> </table> <p>It's a tiny difference but it reads slightly better to me and makes the book slghtly better. Now repeat that for all 3000-odd sentences in the book and I'm done with copyediting!</p> <p>For the first pass, anyway. Copyediting is miserable. </p> <p>Some of the changes I need to make come from reader feedback, but most will come from going through it line-by-line with a copyeditor. Someone's kindly offered to do some of this for free, but I want to find a professional too. If you know anybody, let me know.</p> <h4>Formatting</h4> <p>The book, if I'm being honest, looks ugly. I'm using the default sphinx/latex combination for layout and typesetting. My thinking is it's not worth making the book pretty until it's worth reading. But I also want the book, when it's eventually printed, to look <em>nice</em>. At the very least it shouldn't have "self-published" vibes. </p> <p>I've found someone who's been giving me excellent advice on layout and I'm slowly mastering the LaTeX formatting arcana. It's gonna take a few iterations to get things right.</p> <h4>Front cover</h4> <p>Currently the front cover is this:</p> <p><img alt="Front cover" class="newsletter-image" src="https://assets.buttondown.email/images/b42ee3de-9d8a-4729-809e-a8739741f0cf.png?w=960&fit=max"/></p> <p>It works but gives "programmer spent ten minutes in Inkscape" vibes. I have a vision in my head for what would be nicer. A few people have recommended using Fiverr. So far the results haven't been that good, </p> <h4>Fixing Epub</h4> <p><em>Ugh</em></p> <p>I thought making an epub version would be kinder for phone reading, but it's such a painful format to develop for. Did you know that epub backlinks work totally different on kindle vs other ereaders? Did you know the only way to test if you got em working right is to load them up in a virtual kindle? The feedback loops are miserable. So I've been treating epub as a second-class citizen for now and only fixing the <em>worst</em> errors (like math not rendering properly), but that'll have to change as the book finalizes.</p> <h3>What comes next?</h3> <p>After 1.0, I get my book an ISBN and figure out how to make print copies. The margin on print is <em>way</em> lower than ebooks, especially if it's on-demand: the net royalties for <a href="https://kdp.amazon.com/en_US/help/topic/G201834330" target="_blank">Amazon direct publishing</a> would be 7 dollars on a 20-dollar book (as opposed to Leanpub's 16 dollars). Would having a print version double the sales? I hope so! Either way, a lot of people have been asking about print version so I want to make that possible.</p> <p>(I also want to figure out how to give people who already have the ebook a discount on print, but I don't know if that's feasible.)</p> <p>Then, I dunno, maybe make a talk or a workshop I can pitch to conferences. Once I have that I think I can call <em>LfP</em> complete... at least until the second edition.</p> <hr/> <p>Anyway none of that is actually technical so here's a quick fun thing. I spent a good chunk of my break reading the <a href="https://www.mcrl2.org/web/index.html" target="_blank">mCRL2 book</a>. mCRL2 defines an "algebra" for "communicating processes". As a very broad explanation, that's defining what it means to "add" and "multiply" two processes. What's interesting is that according to their definition, the algebra follows the distributive law, <em>but only if you multiply on the right</em>. eg</p> <div class="codehilite"><pre><span></span><code>// VALID (a+b)*c = a*c + b*c // INVALID a*(b+c) = a*b + a*c </code></pre></div> <p>This is the first time I've ever seen this in practice! Juries still out on the rest of the language.</p> <hr/> <h3>Videos and Stuff</h3> <ul> <li>My <em>DDD Europe</em> talk is now out! <a href="https://www.youtube.com/watch?v=uRmNSuYBUOU" target="_blank">What We Know We Don't Know</a> is about empirical software engineering in general, and software engineering research on Domain Driven Design in particular.</li> <li>I was interviewed in the last video on <a href="https://www.youtube.com/watch?v=yXxmSI9SlwM" target="_blank">Craft vs Cruft</a>'s "Year of Formal Methods". Check it out!</li> </ul></description><pubDate>Tue, 07 Jan 2025 18:49:40 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/</guid></item><item><title>Formally modeling dreidel, the sequel</title><link>https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/</link><description><p>Channukah's next week and that means my favorite pastime, complaining about how <a href="https://en.wikipedia.org/wiki/Dreidel#" target="_blank">Dreidel</a> is a bad game. Last year I formally modeled it in <a href="https://www.prismmodelchecker.org/" target="_blank">PRISM</a> to prove the game's not fun. But because I limited the model to only a small case, I couldn't prove the game was <em>truly</em> bad. </p> <p>It's time to finish the job.</p> <p><img alt="A flaming dreidel, from https://pixelsmerch.com/featured/flaming-dreidel-ilan-rosen.html" class="newsletter-image" src="https://assets.buttondown.email/images/61233445-69a7-4fd4-a024-ee0dca0281c1.jpg?w=960&fit=max"/></p> <h2>The Story so far</h2> <p>You can read the last year's newsletter <a href="https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/" target="_blank">here</a> but here are the high-level notes.</p> <h3>The Game of Dreidel</h3> <ol> <li>Every player starts with N pieces (usually chocolate coins). This is usually 10-15 pieces per player.</li> <li>At the beginning of the game, and whenever the pot is empty, every play antes one coin into the pot.</li> <li> <p>Turns consist of spinning the dreidel. Outcomes are:</p> <ul> <li>נ (Nun): nothing happens.</li> <li>ה (He): player takes half the pot, rounded up.</li> <li>ג (Gimmel): player takes the whole pot, everybody antes.</li> <li>ש (Shin): player adds one of their coins to the pot.</li> </ul> </li> <li> <p>If a player ever has zero coins, they are eliminated. Play continues until only one player remains.</p> </li> </ol> <p>If you don't have a dreidel, you can instead use a four-sided die, but for the authentic experience you should wait eight seconds before looking at your roll.</p> <h3>PRISM</h3> <p><a href="https://www.prismmodelchecker.org/" target="_blank">PRISM</a> is a probabilistic modeling language, meaning you can encode a system with random chances of doing things and it can answer questions like "on average, how many spins does it take before one player loses" (64, for 4 players/10 coins) and "what's the more likely to knock the first player out, shin or ante" (ante is 2.4x more likely). You can see last year's model <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism" target="_blank">here</a>.</p> <p>The problem with PRISM is that it is absurdly inexpressive: it's a thin abstraction for writing giant <a href="https://en.wikipedia.org/wiki/Stochastic_matrix" target="_blank">stochastic matrices</a> and lacks basic affordances like lists or functions. I had to hardcode every possible roll for every player. This meant last year's model had two limits. First, it only handles four players, and I would have to write a new model for three or five players. Second, I made the game end as soon as one player <em>lost</em>:</p> <div class="codehilite"><pre><span></span><code>formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0); </code></pre></div> <p>To fix both of these things, I thought I'd have to treat PRISM as a compilation target, writing a program that took a player count and output the corresponding model. But then December got super busy and I ran out of time to write a program. Instead, I stuck with four hardcoded players and extended the old model to run until victory.</p> <h2>The new model</h2> <p>These are all changes to <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism" target="_blank">last year's model</a>.</p> <p>First, instead of running until one player is out of money, we run until three players are out of money.</p> <div class="codehilite"><pre><span></span><code><span class="gd">- formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0);</span> <span class="gi">+ formula done = </span> <span class="gi">+ ((p1=0) & (p2=0) & (p3=0)) |</span> <span class="gi">+ ((p1=0) & (p2=0) & (p4=0)) |</span> <span class="gi">+ ((p1=0) & (p3=0) & (p4=0)) |</span> <span class="gi">+ ((p2=0) & (p3=0) & (p4=0));</span> </code></pre></div> <p>Next, we change the ante formula. Instead of adding four coins to the pot and subtracting a coin from each player, we add one coin for each player left. <code>min(p1, 1)</code> is 1 if player 1 is still in the game, and 0 otherwise. </p> <div class="codehilite"><pre><span></span><code><span class="gi">+ formula ante_left = min(p1, 1) + min(p2, 1) + min(p3, 1) + min(p4, 1);</span> </code></pre></div> <p>We also have to make sure anteing doesn't end a player with negative money. </p> <div class="codehilite"><pre><span></span><code><span class="gd">- [ante] (pot = 0) & !done -> (pot'=pot+4) & (p1' = p1-1) & (p2' = p2-1) & (p3' = p3-1) & (p4' = p4-1);</span> <span class="gi">+ [ante] (pot = 0) & !done -> (pot'=pot+ante_left) & (p1' = max(p1-1, 0)) & (p2' = max(p2-1, 0)) & (p3' = max(p3-1, 0)) & (p4' = max(p4-1, 0));</span> </code></pre></div> <p>Finally, we have to add logic for a player being "out". Instead of moving to the next player after each turn, we move to the next player still in the game. Also, if someone starts their turn without any coins (f.ex if they just anted their last coin), we just skip their turn. </p> <div class="codehilite"><pre><span></span><code><span class="gi">+ formula p1n = (p2 > 0 ? 2 : p3 > 0 ? 3 : 4);</span> <span class="gi">+ [lost] ((pot != 0) & !done & (turn = 1) & (p1 = 0)) -> (turn' = p1n);</span> <span class="gd">- [spin] ((pot != 0) & !done & (turn = 1)) -></span> <span class="gi">+ [spin] ((pot != 0) & !done & (turn = 1) & (p1 != 0)) -></span> <span class="w"> </span> 0.25: (p1' = p1-1) <span class="w"> </span> & (pot' = min(pot+1, maxval)) <span class="gd">- & (turn' = 2) //shin</span> <span class="gi">+ & (turn' = p1n) //shin</span> </code></pre></div> <p>We make similar changes for all of the other players. You can see the final model <a href="https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-02-dreidel-prism" target="_blank">here</a>.</p> <h3>Querying the model</h3> <div class="subscribe-form"></div> <p>So now we have a full game of Dreidel that runs until the player ends. And now, <em>finally</em>, we can see the average number of spins a 4 player game will last.</p> <div class="codehilite"><pre><span></span><code>./prism<span class="w"> </span>dreidel.prism<span class="w"> </span>-const<span class="w"> </span><span class="nv">M</span><span class="o">=</span><span class="m">10</span><span class="w"> </span>-pf<span class="w"> </span><span class="s1">'R=? [F done]'</span><span class="w"> </span> </code></pre></div> <p>In English: each player starts with ten coins. <code>R=?</code> means "expected value of the 'reward'", where 'reward' in this case means number of spins. <code>[F done]</code> weights the reward over all behaviors that reach ("<strong>F</strong>inally") the <code>done</code> state.</p> <div class="codehilite"><pre><span></span><code>Result: 760.5607582661091 Time for model checking: 384.17 seconds. </code></pre></div> <p>So there's the number: 760 spins.<sup id="fnref:ben"><a class="footnote-ref" href="#fn:ben">1</a></sup> At 8 seconds a spin, that's almost two hours for <em>one</em> game.</p> <p>…Jesus, look at that runtime. Six minutes to test one query.</p> <p>PRISM has over a hundred settings that affect model checking, with descriptions like "Pareto curve threshold" and "Use Backwards Pseudo SOR". After looking through them all, I found this perfect combination of configurations that gets the runtime to a more manageable level: </p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism <span class="w"> </span> -const M=10 <span class="w"> </span> -pf 'R=? [F done]' <span class="gi">+ -heuristic speed</span> Result: 760.816255997373 Time for model checking: 13.44 seconds. </code></pre></div> <p>Yes, that's a literal "make it faster" flag.</p> <p>Anyway, that's only the "average" number of spins, weighted across all games. Dreidel has a very long tail. To find that out, we'll use a variation on our query:</p> <div class="codehilite"><pre><span></span><code>const C0; P=? [F <=C0 done] </code></pre></div> <p><code>P=?</code> is the <strong>P</strong>robability something happens. <code>F <=C0 done</code> means we <strong>F</strong>inally reach state <code>done</code> in at most <code>C0</code> steps. By passing in different values of <code>C0</code> we can get a sense of how long a game takes. Since "steps" includes passes and antes, this will overestimate the length of the game. But antes take time too and it should only "pass" on a player once per player, so this should still be a good metric for game length.</p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -const C0=1000:1000:5000 -pf 'const C0; P=? [F <=C0 done]' -heuristic speed C0 Result 1000 0.6259953274918795 2000 0.9098575028069353 3000 0.9783122218576754 4000 0.994782069562932 5000 0.9987446018004976 </code></pre></div> <p>A full 10% of games don't finish in 2000 steps, and 2% pass the 3000 step barrier. At 8 seconds a roll/ante, 3000 steps is over <strong>six hours</strong>.</p> <p>Dreidel is a bad game.</p> <h3>More fun properties</h3> <p>As a sanity check, let's confirm last year's result, that it takes an average of 64ish spins before one player is out. In that model, we just needed to get the total reward. Now we instead want to get the reward until the first state where any of the players have zero coins. <sup id="fnref:co-safe"><a class="footnote-ref" href="#fn:co-safe">2</a></sup></p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -pf 'R=? [F (p1=0 | p2=0 | p3=0 | p4=0)]' -heuristic speed Result: 63.71310116083396 Time for model checking: 2.017 seconds. </code></pre></div> <p>Yep, looks good. With our new model we can also get the average point where two players are out and two players are left. PRISM's lack of abstraction makes expressing the condition directly a little painful, but we can cheat and look for the first state where <code>ante_left <= 2</code>.<sup id="fnref:ante_left"><a class="footnote-ref" href="#fn:ante_left">3</a></sup></p> <div class="codehilite"><pre><span></span><code>./prism dreidel.prism -const M=10 -pf 'R=? [F (ante_left <= 2)]' -heuristic speed Result: 181.92839196680023 </code></pre></div> <p>It takes twice as long to eliminate the second player as it takes to eliminate the first, and the remaining two players have to go for another 600 spins.</p> <p>Dreidel is a bad game.</p> <h2>The future</h2> <p>There's two things I want to do next with this model. The first is script up something that can generate the PRISM model for me, so I can easily adjust the number of players to 3 or 5. The second is that PRISM has a <a href="https://www.prismmodelchecker.org/manual/PropertySpecification/Filters" target="_blank">filter-query</a> feature I don't understand but I <em>think</em> it could be used for things like "if a player gets 75% of the pot, what's the probability they lose anyway". Otherwise you have to write wonky queries like <code>(P =? [F p1 = 30 & (F p1 = 0)]) / (P =? [F p1 = 0])</code>.<sup id="fnref:lose"><a class="footnote-ref" href="#fn:lose">4</a></sup> But I'm out of time again, so this saga will have to conclude next year.</p> <p>I'm also faced with the terrible revelation that I might be the biggest non-academic user of PRISM.</p> <hr/> <h4><em>Logic for Programmers</em> Khanukah Sale</h4> <p>Still going on! You can get <em>LFP</em> for <a href="https://leanpub.com/logic/c/hannukah-presents" target="_blank">40% off here</a> from now until the end of Xannukkah (Jan 2).<sup id="fnref:joke"><a class="footnote-ref" href="#fn:joke">5</a></sup></p> <h4>I'm in the Raku Advent Calendar!</h4> <p>My piece is called <a href="https://raku-advent.blog/2024/12/11/day-11-counting-up-concurrency/" target="_blank">counting up concurrencies</a>. It's about using Raku to do some combinatorics! Read the rest of the blog too, it's great</p> <div class="footnote"> <hr/> <ol> <li id="fn:ben"> <p>This is different from the <a href="https://www.slate.com/articles/life/holidays/2014/12/rules_of_dreidel_the_hannukah_game_is_way_too_slow_let_s_speed_it_up.html" target="_blank">original anti-Dreidel article</a>: Ben got <em>860</em> spins. That's the average spins if you round <em>down</em> on He, not up. Rounding up on He leads to a shorter game because it means He can empty the pot, which means more antes, and antes are what knocks most players out. <a class="footnote-backref" href="#fnref:ben" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:co-safe"> <p>PRISM calls this <a href="https://www.prismmodelchecker.org/manual/PropertySpecification/Reward-basedProperties" target="_blank">"co-safe LTL reward"</a> and does <em>not</em> explain what that means, nor do most of the papers I found referencing "co-safe LTL". <a href="https://mengguo.github.io/personal_site/papers/pdf/guo2016task.pdf" target="_blank">Eventually</a> I found one that defined it as "any property that only uses X, U, F". <a class="footnote-backref" href="#fnref:co-safe" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:ante_left"> <p>Here's the exact point where I realize I could have defined <code>done</code> as <code>ante_left = 1</code>. Also checking for <code>F (ante_left = 2)</code> gives an expected number of spins as "infinity". I have no idea why. <a class="footnote-backref" href="#fnref:ante_left" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:lose"> <p>10% chances at 4 players / 10 coins. And it takes a minute even <em>with</em> fast mode enabled. <a class="footnote-backref" href="#fnref:lose" title="Jump back to footnote 4 in the text">↩</a></p> </li> <li id="fn:joke"> <p>This joke was funnier before I made the whole newsletter about Chanukahh. <a class="footnote-backref" href="#fnref:joke" title="Jump back to footnote 5 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 18 Dec 2024 16:58:59 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/</guid></item><item><title>Stroustrup's Rule</title><link>https://buttondown.com/hillelwayne/archive/stroustrups-rule/</link><description><p>Just finished two weeks of workshops and am <em>exhausted</em>, so this one will be light. </p> <h3>Hanuka Sale</h3> <p><em>Logic for Programmers</em> is on sale until the end of Chanukah! That's Jan 2nd if you're not Jewish. <a href="https://leanpub.com/logic/c/hannukah-presents" target="_blank">Get it for 40% off here</a>.</p> <h1>Stroustrup's Rule</h1> <p>I first encountered <strong>Stroustrup's Rule</strong> on this <a href="https://web.archive.org/web/20240914141601/https:/www.thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/" target="_blank">defunct webpage</a>:</p> <blockquote> <p>One of my favorite insights about syntax design appeared in a <a href="https://learn.microsoft.com/en-us/shows/lang-next-2014/keynote" target="_blank">retrospective on C++</a><sup id="fnref:timing"><a class="footnote-ref" href="#fn:timing">1</a></sup> by Bjarne Stroustrup:</p> <ul> <li>For new features, people insist on <strong>LOUD</strong> explicit syntax. </li> <li>For established features, people want terse notation.</li> </ul> </blockquote> <p>The blogger gives the example of option types in Rust. Originally, the idea of using option types to store errors was new for programmers, so the syntax for passing an error was very explicit:</p> <div class="codehilite"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">File</span><span class="p">::</span><span class="n">open</span><span class="p">(</span><span class="s">"file.txt"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">file</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="n">file</span><span class="p">,</span> <span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">err</span><span class="p">;</span><span class="w"> </span><span class="p">}</span> <span class="p">}</span> </code></pre></div> <p>Once people were more familiar with it, Rust added the <code>try!</code> macro to reduce boilerplate, and finally the <a href="https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md" target="_blank"><code>?</code> operator</a> to streamline error handling further.</p> <p>I see this as a special case of <a href="http://teachtogether.tech/en/index.html#s:models" target="_blank">mental model development</a>: when a feature is new to you, you don't have an internal mental model so need all of the explicit information you can get. Once you're familiar with it, explicit syntax is visual clutter and hinders how quickly you can parse out information.</p> <p>(One example I like: which is more explicit, <code>user_id</code> or <code>user_identifier</code>? Which do experienced programmers prefer?)</p> <p>What's interesting is that it's often the <em>same people</em> on both sides of the spectrum. Beginners need explicit syntax, and as they become experts, they prefer terse syntax. </p> <p>The rule applies to the overall community, too. At the beginning of a language's life, everybody's a beginner. Over time the ratio of experts to beginners changes, and this leads to more focus on "expert-friendly" features, like terser syntax.</p> <p>This can make it harder for beginners to learn the language. There was a lot of drama in Python over the <a href="https://peps.python.org/pep-0572/" target="_blank">"walrus" assignment operator</a>:</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Without walrus</span> <span class="n">val</span> <span class="o">=</span> <span class="nb">dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="c1"># `None` if key absent</span> <span class="k">if</span> <span class="n">val</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="c1"># With walrus</span> <span class="k">if</span> <span class="n">val</span> <span class="o">:=</span> <span class="nb">dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> </code></pre></div> <p>Experts supported it because it made code more elegant, teachers and beginners opposed it because it made the language harder to learn. Explicit syntax vs terse notation.</p> <p>Does this lead to languages bloating over time?</p> <h3>In Teaching</h3> <p>I find that when I teach language workshops I have to actively work against Stroustrup's Rule. The terse notation that easiest for <em>me</em> to read is bad for beginners, who need the explicit syntax that I find grating.</p> <p>One good example is type invariants in TLA+. Say you have a set of workers, and each worker has a counter. Here's two ways to say that every worker's counter is a non-negative integer:</p> <div class="codehilite"><pre><span></span><code>\* Bad \A w \in Workers: counter[w] >= 0 \* Good counter \in [Workers -> Nat] </code></pre></div> <p>The first way literally tests that for every worker, <code>counter[w]</code> is non-negative. The second way tests that the <code>counter</code> mapping as a whole is an element of the appropriate "function set"— all functions between workers and natural numbers.</p> <p>The function set approach is terser, more elegant, and preferred by TLA+ experts. But I teach the "bad" way because it makes more sense to beginners.</p> <div class="footnote"> <hr/> <ol> <li id="fn:timing"> <p>Starts minute 23. <a class="footnote-backref" href="#fnref:timing" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 11 Dec 2024 17:32:53 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/stroustrups-rule/</guid></item><item><title>Hyperproperties</title><link>https://buttondown.com/hillelwayne/archive/hyperproperties/</link><description><p>I wrote about <a href="https://hillelwayne.com/post/hyperproperties/" target="_blank">hyperproperties on my blog</a> four years ago, but now an intriguing client problem got me thinking about them again.<sup id="fnref:client"><a class="footnote-ref" href="#fn:client">1</a></sup></p> <p>We're using TLA+ to model a system that starts in state A, and under certain complicated conditions <code>P</code>, transitions to state B. They also had a flag <code>f</code> that, when set, used a different complicated condition <code>Q</code> to check the transitions. As a quick <a href="https://www.hillelwayne.com/post/decision-tables/" target="_blank">decision table</a> (from state <code>A</code>):</p> <table> <thead> <tr> <th>f</th> <th>P</th> <th>Q</th> <th>state'</th> </tr> </thead> <tbody> <tr> <td>F</td> <td>F</td> <td>-</td> <td>A</td> </tr> <tr> <td>F</td> <td>T</td> <td>-</td> <td>B</td> </tr> <tr> <td>T</td> <td>F</td> <td>F</td> <td>A</td> </tr> <tr> <td>T</td> <td>F</td> <td>T</td> <td>B</td> </tr> <tr> <td>T</td> <td>T</td> <td>F</td> <td><strong>impossible</strong></td> </tr> <tr> <td>T</td> <td>T</td> <td>T</td> <td>B</td> </tr> </tbody> </table> <p>The interesting bit is the second-to-last row: Q has to be <em>strictly</em> more permissible than P. The client wanted to verify the property that "the system more aggressively transitions when <code>f</code> is set", ie there is no case where the machine transitions <em>only if <code>f</code> is false</em>.</p> <p><a href="https://www.hillelwayne.com/post/safety-and-liveness/" target="_blank">Regular system properties</a> are specified over states in a single sequence of states (behaviors). <strong>Hyperproperties</strong> can hold over <em>sets</em> of sequences of states. Here the hyperproperties are:</p> <blockquote> <ol> <li>For any two states X and Y in separate behaviors, if the only difference in variable-state between X and Y is that <code>X.f = TRUE</code>, then whenever Y transitions to B, so does X.</li> <li>There is at least one such case where X transitions and Y does not.</li> </ol> </blockquote> <p>That's pretty convoluted, which is par for the course with hyperproperties! It makes a little more sense if you have all of the domain knowledge and specifics. </p> <p>The key thing is that makes this a hyperproperty is that you can't <em>just</em> look at individual behaviors to verify it. Imagine if, when <code>f</code> is true, we <em>never</em> transition to state B. Is that a violation of (1)? Not if we never transition when <code>f</code> is false either! To prove a violation, you need to find a behavior where <code>f</code> is false <em>and</em> the state is otherwise the same <em>and</em> we transition to B anyway.</p> <h4>Aside: states in states in states</h4> <p>I dislike how "state" refers to three things:</p> <ol> <li>The high-level "transition state" of a state-machine</li> <li>A single point in time of a system (the "state space")</li> <li>The mutable data inside your system's <a href="https://www.hillelwayne.com/post/world-vs-machine/" target="_blank">machine</a>.</li> </ol> <p>These are all "close" to each other but <em>just</em> different enough to make conversations confusing. Software is pretty bad about reusing colloquial words like this; don't even get me <em>started</em> on the word "design".</p> <h3>There's a reason we don't talk about hyperproperties</h3> <p>Or three reasons. First of all, hyperproperties make up a <em>vanishingly small</em> percentage of the stuff in a system we care about. We only got to "<code>f</code> makes the system more aggressive" after checking at least a dozen other simpler and <em>more important</em> not-hyper properties.</p> <p>Second, <em>most</em> formal specification languages can't express hyperproperties, and the ones that can are all academic research projects. Modeling systems is hard enough without a generalized behavior notation!</p> <p>Third, hyperproperties are astoundingly expensively to check. As an informal estimation, for a state space of size <code>N</code> regular properties are checked across <code>N</code> individual states and 2-behavior hyperproperties (2-props) are checked across <code>N²</code> pairs. So for a small state space of just a million states, the 2-prop needs to be checked across a <em>trillion</em> pairs. </p> <p>These problems don't apply to "hyperproperties" of functions, just systems. Functions have a lot of interesting hyperproperties, there's an easy way to represent them (call the function twice in a test), and quadratic scaling isn't so bad if you're only testing 100 inputs or so. That's why so-called <a href="https://www.hillelwayne.com/post/metamorphic-testing/" target="_blank">metamorphic testing</a> of functions can be useful.</p> <h3>Checking Hyperproperties Anyway</h3> <p>If we <em>do</em> need to check a hyperproperty, there's a few ways we can approach it. </p> <p>The easiest way is to cheat and find a regular prop that implies the hyperproperty. In client's case, we can abstract <code>P</code> and <code>Q</code> into pure functions and then test that there's no input where <code>P</code> is true and <code>Q</code> is false. In TLA+, this would look something like</p> <div class="codehilite"><pre><span></span><code>\* TLA+ QLooserThanP == \A i1 \in InputSet1, i2 \in Set2: \* ... P(i1, i2, …) => Q(i1, i2, …) </code></pre></div> <p>Of course we can't always encapsulate this way, and this can't catch bugs like "we accidentally use <code>P</code> even if <code>f</code> is true". But it gets the job done.</p> <p>Another way is something I talked about in the <a href="https://hillelwayne.com/post/hyperproperties/" target="_blank">original hyperproperty post</a>: lifting specs into hyperspecs. We create a new spec that initializes two copies of our main spec, runs them in parallel, and then compares their behaviors. See the post for an example. Writing a hyperspec keeps us entirely in TLA+ but takes a lot of work and is <em>very</em> expensive to check. Depending on the property we want to check, we can sometimes find simple optimizations.</p> <p>The last way is something <a href="https://hillelwayne.com/post/graphing-tla/" target="_blank">I explored last year</a>: dump the state graph to disk and treat the hyperproperty as a graph property. In this case, the graph property would be something like </p> <blockquote> <p>Find all graph edges representing an A → B transition. Take all the source nodes of each where <code>f = false</code>. For each such source node, find the corresponding node that's identical except for <code>f = true</code>. That node should be the source of an A → B edge.</p> </blockquote> <p>Upside is you don't have to make any changes to the original spec. Downside is you have to use another programming language for analysis. Also, <a href="https://hillelwayne.com/post/graph-types/" target="_blank">analyzing graphs is terrible</a>. But I think this overall the most robust approach to handling hyperproperties, to be used when "cheating" fails.</p> <hr/> <p>What fascinates me most about this is the four-year gap between "I learned and wrote about hyperproperties" and "I have to deal with hyperproperties in my job." This is one reason learning for the sake of learning can have a lot of long-term benefits.</p> <hr/> <h3>Blog Rec</h3> <p>This week's rec is <a href="https://robertheaton.com/" target="_blank">Robert Heaton</a>. It's a "general interest" software engineering blog with a focus on math, algorithms, and security. Some of my favorites:</p> <ul> <li><a href="https://robertheaton.com/preventing-impossible-game-levels-using-cryptography/" target="_blank">Preventing impossible game levels using cryptography</a> and the whole "Steve Steveington" series</li> <li><a href="https://robertheaton.com/2019/06/24/i-was-7-words-away-from-being-spear-phished/" target="_blank">I was 7 words away from being spear-phished</a> is a great deep dive into one targeted scam</li> <li><a href="https://robertheaton.com/2019/02/24/making-peace-with-simpsons-paradox/" target="_blank">Making peace with Simpson's Paradox</a> is the best explanation of Simpson's Paradox I've ever read.</li> </ul> <p>Other good ones are <a href="https://robertheaton.com/pyskywifi/" target="_blank">PySkyWiFi: completely free, unbelievably stupid wi-fi on long-haul flights</a> and <a href="https://robertheaton.com/interview/" target="_blank">How to pass a coding interview with me</a>. The guy's got <em>breadth</em>.</p> <div class="footnote"> <hr/> <ol> <li id="fn:client"> <p>I do formal methods consulting btw. <a href="https://www.hillelwayne.com/consulting/" target="_blank">Hire me!</a> <a class="footnote-backref" href="#fnref:client" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 19 Nov 2024 19:34:54 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/hyperproperties/</guid></item><item><title>Five Unusual Raku Features</title><link>https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/</link><description><h3><a href="https://leanpub.com/logic/" target="_blank"><em>Logic for Programmers</em></a> is now in Beta!</h3> <p><a href="https://leanpub.com/logic/" target="_blank">v0.5 marks the official end of alpha</a>! With the new version, all of the content I wanted to put in the book is now present, and all that's left is copyediting, proofreading, and formatting. Which will probably take as long as it took to actually write the book. You can see the release notes in the footnote.<sup id="fnref:release-notes"><a class="footnote-ref" href="#fn:release-notes">1</a></sup></p> <p>And I've got a snazzy new cover:</p> <p><img alt="The logic for programmers cover, a 40x zoom of a bird feather" class="newsletter-image" src="https://assets.buttondown.email/images/26c75f1e-e60a-4328-96e5-9878d96d3e53.png?w=960&fit=max"/></p> <p>(I don't actually like the cover that much but it <em>looks</em> official enough until I can pay an actual cover designer.)</p> <h1>"Five" Unusual Raku Features</h1> <p>Last year I started learning Raku, and the sheer bizarreness of the language left me describing it as <a href="https://buttondown.com/hillelwayne/archive/raku-a-language-for-gremlins/" target="_blank">a language for gremlins</a>. Now that I've used it in anger for over a year, I have a better way of describing it:</p> <blockquote> <p>Raku is a laboratory for language features.</p> </blockquote> <p>This is why it has <a href="https://docs.raku.org/language/concurrency" target="_blank">five different models of concurrency</a> and eighteen ways of doing anything else, because the point is to <em>see</em> what happens. It also explains why many of the features interact so strangely and why there's all that odd edge-case behavior. Getting 100 experiments polished and playing nicely with each other is much harder than running 100 experiments; we can sort out the polish <em>after</em> we figure out which ideas are good ones.</p> <p>So here are "five" Raku experiments you could imagine seeing in another programming language. If you squint.</p> <h3><a href="https://docs.raku.org/type/Junction" target="_blank">Junctions</a></h3> <p>Junctions are "superpositions of possible values". Applying an operation to a junction instead applies it to every value inside the junction. </p> <div class="codehilite"><pre><span></span><code>> <span class="mi">2</span><span class="o">|</span><span class="mi">10</span> <span class="nb">any</span>(<span class="mi">2</span>, <span class="mi">10</span>) > <span class="mi">2</span><span class="nv">&10</span> + <span class="mi">3</span> <span class="nb">all</span>(<span class="mi">5</span>, <span class="mi">13</span>) >(<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) <span class="nb">all</span>(<span class="nb">one</span>(<span class="mi">11</span>, <span class="mi">21</span>), <span class="nb">one</span>(<span class="mi">12</span>, <span class="mi">22</span>)) </code></pre></div> <p>As you can probably tell from the <code>all</code>s and <code>any</code>s, junctions are a feature meant for representing boolean formula. There's no way to destructure a junction, and the only way to use it is to collapse it to a boolean first.</p> <div class="codehilite"><pre><span></span><code>> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) < <span class="mi">15</span> <span class="nb">all</span>(<span class="nb">one</span>(<span class="nb">True</span>, <span class="nb">False</span>), <span class="nb">one</span>(<span class="nb">True</span>, <span class="nb">False</span>)) <span class="c1"># so coerces junctions to booleans</span> > <span class="nb">so</span> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) < <span class="mi">15</span> <span class="nb">True</span> > <span class="nb">so</span> (<span class="mi">1</span><span class="nv">&2</span>) + (<span class="mi">10</span><span class="o">^</span><span class="mi">20</span>) > <span class="mi">0</span> <span class="nb">False</span> > <span class="mi">16</span> %% (<span class="mi">3</span><span class="nv">&5</span>) ?? <span class="s">"fizzbuzz"</span> !! * * </code></pre></div> <p>The real interesting thing for me is how Raku elegantly uses junctions to represent quantifiers. In most languages, you either have the function <code>all(list[T], T -> bool)</code> or the method <code>[T].all(T -> bool)</code>, both of which apply the test to every element of the list. In Raku, though, <code>list.all</code> doesn't take <em>anything</em>, it's just a niladic method that turns the list into a junction. </p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">$x</span> = <span class="s"><1 2 3></span>.<span class="nb">all</span> <span class="nb">all</span>(<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>) > <span class="nb">is-prime</span>(<span class="nv">$x</span>) <span class="nb">all</span>(<span class="nb">False</span>, <span class="nb">True</span>, <span class="nb">True</span>) </code></pre></div> <p>This means we can combine junctions. If Raku didn't already have a <code>unique</code> method, we could build it by saying "are all elements equal to exactly one element?"</p> <div class="codehilite"><pre><span></span><code>> <span class="nb">so</span> {.<span class="nb">all</span> == .<span class="nb">one</span>}(<span class="s"><1 2 3 7></span>) <span class="nb">True</span> > <span class="nb">so</span> {.<span class="nb">all</span> == .<span class="nb">one</span>}(<span class="s"><1 2 3 7 2></span>) <span class="nb">False</span> </code></pre></div> <h3><a href="https://docs.raku.org/type/Whatever" target="_blank">Whatevers</a></h3> <p><code>*</code> is the "whatever" symbol and has a lot of different roles in Raku.<sup id="fnref:analogs"><a class="footnote-ref" href="#fn:analogs">2</a></sup> Some functions and operators have special behavior when passed a <code>*</code>. In a range or sequence, <code>*</code> means "unbound".</p> <div class="codehilite"><pre><span></span><code>> <span class="mi">1</span>..* <span class="mi">1</span><span class="o">..</span><span class="n">Inf</span> > (<span class="mi">2</span>,<span class="mi">4</span>,<span class="mi">8</span>...*)[<span class="mi">17</span>] <span class="mi">262144</span> </code></pre></div> <p>The main built-in use, though, is that expressions with <code>*</code> are lifted into anonymous functions. This is called "whatever-priming" and produces a <code>WhateverCode</code>, which is indistinguishable from other functions except for the type.</p> <div class="codehilite"><pre><span></span><code>> {<span class="nv">$_</span> + <span class="mi">10</span>}(<span class="mi">2</span>) <span class="mi">12</span> > (* + <span class="mi">10</span>)(<span class="mi">2</span>) <span class="mi">12</span> > (^<span class="mi">10</span>).<span class="n">map</span>(* % <span class="mi">2</span>) (<span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span>) </code></pre></div> <p>There's actually a bit of weird behavior here: if <em>two</em> whatevers appear in the expression, they become separate positional variables. <code>(2, 30, 4, 50).map(* + *)</code> returns <code>(32, 54)</code>. This makes it easy to express <a href="https://docs.raku.org/language/operators#infix_..." target="_blank">a tricky Fibonacci definition</a> but otherwise I don't see how it's better than making each <code>*</code> the same value.</p> <p>Regardless, priming is useful because <em>so many</em> Raku methods are overloaded to take functions. You get the last element of a list with <code>l[*-1]</code>. This <em>looks</em> like standard negative-index syntax, but what actually happens is that when <code>[]</code> is passed a function, it passes in list length and looks up the result. So if the list has 10 elements, <code>l[*-1] = l[10-1] = l[9]</code>, aka the last element. Similarly, <code>l.head(2)</code> is the first two elements of a list, <code>l.head(*-2)</code> is all-but-the-last-two.</p> <p>We can pass other functions to <code>[]</code>, which e.g. makes implementing ring buffers easy.</p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">@x</span> = ^<span class="mi">10</span> [<span class="mi">0</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">5</span> <span class="mi">6</span> <span class="mi">7</span> <span class="mi">8</span> <span class="mi">9</span>] > <span class="nv">@x</span>[<span class="mi">95</span> % *]--; <span class="nv">@x</span> [<span class="mi">0</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">4</span> <span class="mi">6</span> <span class="mi">7</span> <span class="mi">8</span> <span class="mi">9</span>] </code></pre></div> <h3><a href="https://docs.raku.org/language/regexes" target="_blank">Regular Expressions</a></h3> <p>There are two basic standards for regexes: POSIX regexes and Perl-compatible regexes (PCRE). POSIX regexes are a terrible mess of backslashes and punctuation. PCRE is backwards compatible with POSIX and is a more terrible mess of backslashes and punctuation. Most languages follow the PCRE standard, but Perl 6 breaks backwards compatibility with an entirely new regex syntax. </p> <p>The most obvious improvement: <a href="https://docs.raku.org/language/regexes#Subrules" target="_blank">composability</a>. In most languages "combine" two regexes by concating their strings together, which is terrible for many, many reasons. Raku has the standard "embed another regex" syntax: <code>/< foo >+/</code> matches one-or-more of the <code>foo</code> regex without <code>foo</code> "leaking" into the top regex. </p> <p>This already does a lot to make regexes more tractable: you can break a complicated regular expression down into simpler and more legible parts. And in fact this is how Raku supports <a href="https://docs.raku.org/language/grammars" target="_blank">parsing grammars</a> as a builtin language feature. I've only used grammars once but it <a href="https://www.hillelwayne.com/post/picat/" target="_blank">was quite helpful</a>.</p> <p>Since we're breaking backwards compatibility anyway, we can now add lots of small QOLs. There's a <a href="https://docs.raku.org/language/regexes#Modified_quantifier:_%,_%%" target="_blank">value separator</a> modifier: <code>\d+ % ','</code> matches <code>1</code> / <code>1,2</code> / <code>1,1,4</code> but not <code>1,</code> or <code>12</code>. <a href="https://docs.raku.org/language/regexes#Lookaround_assertions" target="_blank">Lookaheads</a> and non-capturing groups aren't nonsense glyphs. <code>r1 && r2</code> only matches strings that match <em>both</em> <code>r1</code> and <code>r2</code>. Backtracking can be stopped with <a href="https://docs.raku.org/language/regexes#Preventing_backtracking:_:" target="_blank">:</a>. Whitespace is ignored by default and has to be explicitly enabled in match patterns.</p> <p>There's more stuff Raku does with actually <em>processing</em> regular expressions, but the regex notation is something that might actually appear in another language someday. </p> <p style="height:16px; margin:0px !important;"></p> <h3><a href="https://docs.raku.org/language/operators#Hyper_operators" target="_blank">Hyperoperators</a></h3> <p>This is a small one compared to the other features, but it's also the thing I miss most often in other languages. The most basic form <code>l>>.method</code> is basically equivalent to <code>map</code>, except it also recursively descends into sublists.</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>]>>.<span class="nb">succ</span> [<span class="mi">2</span> [<span class="mi">3</span> <span class="mi">4</span>] <span class="mi">5</span>] </code></pre></div> <p>This is more useful than it looks because any function call <code>f(list, *args)</code> can be rewritten in "method form" <code>list.&f(*args)</code>, so <code>>>.</code> becomes the generalized mapping operator. You can use it with whatevers, too.</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>]>>.&(*+<span class="mi">1</span>) [<span class="mi">2</span> [<span class="mi">3</span> <span class="mi">4</span>] <span class="mi">5</span>] </code></pre></div> <p>Anyway, the more generalized <em>binary</em> hyperoperator <code>l1 << op >> l2</code><sup id="fnref:spaces"><a class="footnote-ref" href="#fn:spaces">3</a></sup> applies <code>op</code> elementwise to the two lists, looping the shorter list until the longer list is exhausted. <code>>>op>></code> / <code><< op<<</code> are the same except they instead loop until the lhs/rhs list is exhausted. Whew!</p> <div class="codehilite"><pre><span></span><code>> [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> <span class="mi">22</span> <span class="mi">13</span> <span class="mi">24</span> <span class="mi">15</span>] > [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+<< [10, 20]</span> <span class="s">[11 22]</span> <span class="s">> [1, 2, 3, 4, 5] >></span>+>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> <span class="mi">22</span> <span class="mi">13</span> <span class="mi">24</span> <span class="mi">15</span>] <span class="c1"># Also works with single values</span> > [<span class="mi">1</span>, <span class="mi">2</span>, <span class="mi">3</span>, <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> <span class="mi">10</span> [<span class="mi">11</span> <span class="mi">12</span> <span class="mi">13</span> <span class="mi">14</span> <span class="mi">15</span>] <span class="c1"># Does weird things with nested lists too</span> > [<span class="mi">1</span>, [<span class="mi">2</span>, <span class="mi">3</span>], <span class="mi">4</span>, <span class="mi">5</span>] <span class="s"><<+></span>> [<span class="mi">10</span>, <span class="mi">20</span>] [<span class="mi">11</span> [<span class="mi">22</span> <span class="mi">23</span>] <span class="mi">14</span> <span class="mi">25</span>] </code></pre></div> <p>Also for some reason the hyperoperators have separate behaviors on two hashes, either applying <code>op</code> to the union/intersection/hash difference. </p> <p>Anyway it's a super weird (meta)operator but it's also quite useful! It's the closest thing I've seen to <a href="https://hillelwayne.com/post/j-notation/" target="_blank">J verbs</a> outside an APL. I like using it to run the same formula on multiple possible inputs at once.</p> <div class="codehilite"><pre><span></span><code>(<span class="mi">20</span> * <span class="mi">10</span> <span class="s"><<-></span>> (<span class="mi">21</span>, <span class="mi">24</span>)) <span class="s"><<*></span>> (<span class="mi">10</span>, <span class="mi">100</span>) (<span class="mi">1790</span> <span class="mi">17600</span>) </code></pre></div> <p>Incidentally, it's called the hyperoperator because it evaluates all of the operations in parallel. Explicit loops can be parallelized by prefixing them with <a href="https://docs.raku.org/language/statement-prefixes#hyper,_race" target="_blank"><code>hyper</code></a>.</p> <h3><a href="https://docs.raku.org/type/Pair" target="_blank">Pair Syntax</a></h3> <p>I've talked about pairs a little in <a href="https://buttondown.com/hillelwayne/archive/unusual-basis-types-in-programming-languages/" target="_blank">this newsletter</a>, but the gist is that Raku hashes are composed of a set of pairs <code>key => value</code>. The pair is the basis type, the hash is the collection of pairs. There's also a <em>ton</em> of syntactic sugar for concisely specifying pairs via "colon syntax":</p> <div class="codehilite"><pre><span></span><code>> <span class="k">my</span> <span class="nv">$x</span> = <span class="mi">3</span>; :<span class="nv">$x</span> <span class="nb">x</span> => <span class="mi">3</span> > :<span class="n">a</span><span class="s"><$x></span> <span class="n">a</span> => <span class="s">"$x"</span> > :<span class="n">a</span>(<span class="nv">$x</span>) <span class="n">a</span> => <span class="mi">3</span> > :<span class="mi">3</span><span class="n">a</span> <span class="n">a</span> => <span class="mi">3</span> </code></pre></div> <p>The most important sugars are <code>:key</code> and <code>:!key</code>, which map to <code>key => True</code> and <code>key => False</code>. This is a really elegant way to add flags to a methods! Take the definition of <a href="https://docs.raku.org/type/Str#method_match" target="_blank">match</a>:</p> <div class="codehilite"><pre><span></span><code><span class="k">method</span> <span class="nb">match</span>(<span class="nv">$pat</span>, :<span class="n">continue</span>(:<span class="nv">$c</span>), :<span class="n">pos</span>(:<span class="nv">$p</span>), :<span class="n">global</span>(:<span class="nv">$g</span>), :<span class="n">overlap</span>(:<span class="nv">$ov</span>), :<span class="n">exhaustive</span>(:<span class="nv">$ex</span>), :<span class="n">st</span>(:<span class="nv">$nd</span>), :<span class="n">rd</span>(:<span class="nv">$th</span>), :<span class="nv">$nth</span>, :<span class="nv">$x</span> --> <span class="nb">Match</span>) </code></pre></div> <p>Probably should also mention that in a definition, <code>:f(:$foo)</code> defines the parameter <code>$foo</code> but <a href="https://docs.raku.org/language/signatures#Argument_aliases" target="_blank">also aliases it</a> to <code>:f</code>, so you can set the flag with <code>:f</code> or <code>:foo</code>. Colon-pairs defined in the signature can be passed in anywhere, or even stuck together:</p> <div class="codehilite"><pre><span></span><code>> <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>) 「<span class="n">ab</span>」 > <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>, :<span class="n">g</span>) (「<span class="n">ab</span>」 「<span class="n">ab</span>」) > <span class="s">"abab"</span>.<span class="nb">match</span>(<span class="sr">/../</span>, :<span class="n">g</span>, :<span class="n">ov</span>) (「<span class="n">ab</span>」 「<span class="n">ba</span>」 「<span class="n">ab</span>」) <span class="c1"># Out of order stuck together</span> > <span class="s">"abab"</span>.<span class="nb">match</span>(:<span class="n">g:ov</span>,<span class="sr"> /../</span>) (「<span class="n">ab</span>」 「<span class="n">ba</span>」 「<span class="n">ab</span>」) </code></pre></div> <p>So that leads to extremely concise method configuration. Definitely beats <code>match(global=True, overlap=True)</code>!</p> <p>And for some reason you can place keyword arguments <em>after</em> the function call:</p> <div class="codehilite"><pre><span></span><code>> <span class="s">"abab"</span>.<span class="nb">match</span>(:<span class="n">g</span>,<span class="sr"> /../</span>):<span class="n">ov:2nd</span> 「<span class="n">ba</span>」 </code></pre></div> <h2>The next-gen lab: Slangs and RakuAST</h2> <p>These are features I have no experience in and <em>certainly</em> are not making their way into other languages, but they really expand the explorable space of new features. <a href="https://raku.land/zef:lizmat/Slangify" target="_blank">Slangs</a> are modifications to the Raku syntax. This can be used for things like <a href="https://raku.land/zef:elcaro/Slang::Otherwise" target="_blank">modifying loop syntax</a>, <a href="https://raku.land/zef:raku-community-modules/Slang::Piersing" target="_blank">changing identifiers</a>, or adding <a href="https://raku.land/zef:raku-community-modules/OO::Actors" target="_blank">actors</a> or <a href="https://raku.land/github:MattOates/BioInfo" target="_blank">DNA sequences</a> to the base language.</p> <p>I <em>barely</em> understand <a href="https://dev.to/lizmat/rakuast-for-early-adopters-576n" target="_blank">RakuAST</a>. I <em>think</em> the idea is that all Raku expressions can be parsed as an AST from inside Raku itself.</p> <div class="codehilite"><pre><span></span><code>> <span class="s">Q/my $x; $x++/</span>.<span class="nb">AST</span> <span class="n">RakuAST::StatementList</span>.<span class="nb">new</span>( <span class="n">RakuAST::Statement::Expression</span>.<span class="nb">new</span>( <span class="n">expression</span> => <span class="n">RakuAST::VarDeclaration::Simple</span>.<span class="nb">new</span>( <span class="nb">sigil</span> => <span class="s">"\$"</span>, <span class="n">desigilname</span> => <span class="n">RakuAST::Name</span>.<span class="n">from-identifier</span>(<span class="s">"x"</span>) ) ), <span class="n">RakuAST::Statement::Expression</span>.<span class="nb">new</span>( <span class="n">expression</span> => <span class="n">RakuAST::ApplyPostfix</span>.<span class="nb">new</span>( <span class="n">operand</span> => <span class="n">RakuAST::Var::Lexical</span>.<span class="nb">new</span>(<span class="s">"\$x"</span>), <span class="nb">postfix</span> => <span class="n">RakuAST::Postfix</span>.<span class="nb">new</span>(<span class="s">"++"</span>) ) ) ) </code></pre></div> <p>This allows for things like writing Raku in different languages:</p> <div class="codehilite"><pre><span></span><code><span class="nb">say</span> <span class="s">Q/my $x; put $x/</span>.<span class="nb">AST</span>.<span class="n">DEPARSE</span>(<span class="s">"NL"</span>) <span class="n">mijn</span> <span class="nv">$x</span>; <span class="n">zeg-het</span> <span class="nv">$x</span> </code></pre></div> <h3>Bonus experiment</h3> <p>Raku comes with a "<a href="https://rakudo.org/star" target="_blank">Rakudo Star</a>" installation, which comes with a set of <a href="https://github.com/rakudo/star/blob/master/etc/modules.txt" target="_blank">blessed third party modules</a> preinstalled. I love this! It's a great compromise between the maintainer burdens of a large standard library and the user burdens of making everybody find the right packages in the ecosystem.</p> <hr/> <h2>Blog Rec</h2> <p>Feel obligated to recommend some Raku blogs! Elizabeth Mattijsen posts <a href="https://dev.to/lizmat" target="_blank">a ton of stuff</a> to dev.to about Raku internals. <a href="https://www.codesections.com/blog/" target="_blank">Codesections</a> has a pretty good blog; he's the person who eventually got me to try out Raku. Finally, the <a href="https://raku-advent.blog/" target="_blank">Raku Advent Calendar</a> is a great dive into advanced Raku techniques. Bad news is it only updates once a year, good news is it's 25 updates that once a year.</p> <div class="footnote"> <hr/> <ol> <li id="fn:release-notes"> <ul> <li>All techniques chapters now have a "Further Reading" section</li> <li>"System modeling" chapter significantly rewritten</li> <li>"Conditionals" chapter expanded, now a real chapter</li> <li>"Logic Programming" chapter now covers datalog, deductive databases</li> <li>"Solvers" chapter has diagram explaining problem</li> <li>Eight new exercises</li> <li>Tentative front cover (will probably change)</li> <li>Fixed some epub issues with math rendering</li> </ul> <p><a class="footnote-backref" href="#fnref:release-notes" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:analogs"> <p>Analogues are <a href="https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala/8001065#8001065" target="_blank">Scala's underscore</a>, except unlike Scala it's a value and not syntax, and like Python's <a href="https://docs.python.org/3/library/constants.html#Ellipsis" target="_blank">Ellipses</a>, except it has additional semantics. <a class="footnote-backref" href="#fnref:analogs" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:spaces"> <p>Spaces added so buttondown doesn't think they're tags <a class="footnote-backref" href="#fnref:spaces" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 12 Nov 2024 20:06:55 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/</guid></item><item><title>A list of ternary operators</title><link>https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/</link><description><p>Sup nerds, I'm back from SREcon! I had a blast, despite knowing nothing about site reliability engineering and being way over my head in half the talks. I'm trying to catch up on <a href="https://leanpub.com/logic/" target="_blank">The Book</a> and contract work now so I'll do something silly here: ternary operators.</p> <p>Almost all operations on values in programming languages fall into one of three buckets: </p> <ol> <li><strong>Unary operators</strong>, where the operator goes <em>before</em> or <em>after</em> exactly one argument. Examples are <code>x++</code> and <code>-y</code> and <code>!bool</code>. Most languages have a few critical unary operators hardcoded into the grammar. They are almost always symbols, but sometimes are string-identifiers (<code>not</code>).</li> <li><strong>Binary operators</strong>, which are placed <em>between</em> exactly two arguments. Things like <code>+</code> or <code>&&</code> or <code>>=</code>. Languages have a lot more of these than unary operators, because there's more fundamental things we want to do with two values than one value. These can be symbols or identifiers (<code>and</code>).</li> <li>Functions/methods that <em>prefix</em> any number of arguments. <code>func(a, b, c)</code>, <code>obj.method(a, b, c, d)</code>, anything in a lisp. These are how we extend the language, and they almost-exclusively use identifiers and not symbols.<sup id="fnref:lisp"><a class="footnote-ref" href="#fn:lisp">1</a></sup></li> </ol> <p>There's one widespread exception to this categorization: the <strong>ternary operator</strong> <code>bool ? x : y</code>.<sup id="fnref:ternary"><a class="footnote-ref" href="#fn:ternary">2</a></sup> It's an infix operator that takes exactly <em>three</em> arguments and can't be decomposed into two sequential binary operators. <code>bool ? x</code> makes no sense on its own, nor does <code>x : y</code>. </p> <p>Other ternary operators are <em>extremely</em> rare, which is why conditional expressions got to monopolize the name "ternary". But I like how exceptional they are and want to compile some of them. A long long time ago I asked <a href="https://twitter.com/hillelogram/status/1378509881498603527" target="_blank">Twitter</a> for other ternary operators; this is a compilation of some applicable responses plus my own research.</p> <p>(Most of these are a <em>bit</em> of a stretch.)</p> <h3>Stepped Ranges</h3> <p>Many languages have some kind of "stepped range" function:</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Python</span> <span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">]</span> </code></pre></div> <p>There's the "base case" of start and endpoints, and an optional step. Many languages have a binary infix op for the base case, but a few also have a ternary for the optional step:</p> <div class="codehilite"><pre><span></span><code># Frink > map[{|a| a*2}, (1 to 100 step 15) ] [2, 32, 62, 92, 122, 152, 182] # Elixir > IO.puts Enum.join(1..10//2, " ") 1 3 5 7 9 </code></pre></div> <p>This isn't decomposable into two binary ops because you can't assign the range to a value and then step the value later.</p> <h3>Graph ops</h3> <p>In <a href="https://graphviz.org/" target="_blank">Graphviz</a>, a basic edge between two nodes is either the binary <code>node1 -> node2</code> or the ternary <code>node1 -> node2 [edge_props]</code>:</p> <div class="codehilite"><pre><span></span><code><span class="k">digraph</span><span class="w"> </span><span class="nt">G</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">a1</span><span class="w"> </span><span class="o">-></span><span class="w"> </span><span class="nt">a2</span><span class="w"> </span><span class="p">[</span><span class="na">color</span><span class="p">=</span><span class="s2">"green"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div> <p><img alt="Output of the above graphviz" class="newsletter-image" src="https://assets.buttondown.email/images/d1a0f894-59d5-45d3-8702-967e94672371.png?w=960&fit=max"/></p> <p>Graphs seem ternary-friendly because there are three elements involved with any graph connection: the two nodes and the connecting edge. So you also see ternaries in some graph database query languages, with separate places to specify each node and the edge.</p> <div class="codehilite"><pre><span></span><code># GSQL (https://docs.tigergraph.com/gsql-ref/4.1/tutorials/gsql-101/parameterized-gsql-query) SELECT tgt FROM start:s -(Friendship:e)- Person:tgt; # Cypher (https://neo4j.com/docs/cypher-manual/current/introduction/cypher-overview/) MATCH (actor:Actor)-[:ACTED_IN]->(movie:Movie {title: 'The Matrix'}) </code></pre></div> <p>Obligatory plug for my <a href="https://www.hillelwayne.com/post/graph-types/" target="_blank">graph datatype essay</a>.</p> <h3>Metaoperators</h3> <p>Both <a href="https://raku.org/" target="_blank">Raku</a> and <a href="https://www.jsoftware.com/#/README" target="_blank">J</a> have special higher-order functions that apply to binary infixes. Raku calls them <em>metaoperators</em>, while J calls them <em>adverbs</em> and <em>conjugations</em>.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Raku</span> <span class="c1"># `a «op» b` is map, "cycling" shorter list</span> <span class="nb">say</span> <span class="s"><10 20 30></span> «+» <span class="s"><4 5></span> (<span class="mi">14</span> <span class="mi">25</span> <span class="mi">34</span>) <span class="c1"># `a Rop b` is `b op a`</span> <span class="nb">say</span> <span class="mi">2</span> <span class="n">R-</span> <span class="mi">3</span> <span class="mi">1</span> </code></pre></div> <div class="codehilite"><pre><span></span><code><span class="c1">NB. J</span> <span class="c1">NB. x f/ y creates a "table" of x f y</span> <span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">+/</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="mi">20</span> <span class="mi">11</span><span class="w"> </span><span class="mi">21</span> <span class="mi">12</span><span class="w"> </span><span class="mi">22</span> </code></pre></div> <p>The Raku metaoperators are closer to what I'm looking for, since I don't think you can assign the "created operator" directly to a callable variable. J lets you, though!</p> <div class="codehilite"><pre><span></span><code><span class="w"> </span><span class="nv">h</span><span class="w"> </span><span class="o">=:</span><span class="w"> </span><span class="o">+/</span> <span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nv">h</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="mi">4</span> <span class="mi">4</span><span class="w"> </span><span class="mi">5</span> <span class="mi">5</span><span class="w"> </span><span class="mi">6</span> </code></pre></div> <p>That said, J has some "decomposable" ternaries that feel <em>spiritually</em> like ternaries, like <a href="https://code.jsoftware.com/wiki/Vocabulary/curlyrt#dyadic" target="_blank">amend</a> and <a href="https://code.jsoftware.com/wiki/Vocabulary/fcap" target="_blank">fold</a>. It also has a special ternary-ish contruct called the "fork".<sup id="fnref:ternaryish"><a class="footnote-ref" href="#fn:ternaryish">3</a></sup> <code>x (f g h) y</code> is parsed as <code>(x f y) g (x h y)</code>:</p> <div class="codehilite"><pre><span></span><code><span class="c1">NB. Max - min</span> <span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="o">>.</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="o"><.</span><span class="p">)</span><span class="w"> </span><span class="mi">2</span> <span class="mi">3</span> <span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="p">(</span><span class="o">>.</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="o"><.</span><span class="p">)</span><span class="w"> </span><span class="mi">5</span> <span class="mi">3</span> </code></pre></div> <p>So at the top level that's just a binary operator, but the binary op is constructed via a ternary op. That's pretty cool IMO.</p> <h3>Assignment Ternaries</h3> <p>Bob Nystrom points out that in many languages, <code>a[b] = c</code> is a ternary operation: it is <em>not</em> the same as <code>x = a[b]; x = c</code>.</p> <p>A weirder case shows up in <a href="https://github.com/betaveros/noulith/" target="_blank">Noulith</a> and Raku (again): update operators. Most languages have the <code>+=</code> <em>binary operator</em>, these two have the <code>f=</code> <em>ternary operator</em>. <code>a f= b</code> is the same as <code>a = f(a, b)</code>.</p> <div class="codehilite"><pre><span></span><code><span class="c1"># Raku</span> > <span class="k">my</span> <span class="nv">$x</span> = <span class="mi">2</span>; <span class="nv">$x</span> <span class="nb">max</span>= <span class="mi">3</span>; <span class="nb">say</span> <span class="nv">$x</span> <span class="mi">3</span> </code></pre></div> <p>Arguably this is just syntactic sugar, but I don't think it's decomposable into binary operations.</p> <h3>Custom user ternaries</h3> <p>Tikhon Jelvis pointed out that <a href="https://agda.readthedocs.io/en/v2.7.0.1/language/mixfix-operators.html" target="_blank">Agda</a> lets you define <em>custom</em> mixfix operators, which can be ternary or even tetranary or pentanary. I later found out that <a href="https://docs.racket-lang.org/mixfix/index.html" target="_blank">Racket</a> has this, too. <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html" target="_blank">Objective-C</a> <em>looks</em> like this, too, but feels different somehow. </p> <h3>Near Misses</h3> <p>All of these are arguable, I've just got to draw a line in the sand <em>somewhere</em>.</p> <ul> <li>Regular expression substitutions: <code>s/from/to/flags</code> seems like a ternary, but I'd argue it a datatype constructor, not an expression operator.</li> <li>Comprehensions like <code>[x + 1 | x <- list]</code>: looks like the ternary <code>[expr1 | expr2 <- expr3]</code>, but <code>expr2</code> is only binding a name. Arguably a ternary if you can map <em>and filter</em> in the same expression a la Python or Haskell, but should that be considered sugar for</li> <li>Python's operator chaining (<code>1 < x < 5</code>): syntactic sugar for <code>1 < x and x < 5</code>.</li> <li>Someone suggested <a href="https://stackoverflow.com/questions/7251772/what-exactly-constitutes-swizzling-in-opengl-es-2-0-powervr-sgx-specifically" target="_blank">glsl swizzles</a>, which are very cool but binary operators.</li> </ul> <h2>Why are ternaries so rare?</h2> <p>Ternaries are <em>somewhat</em> more common in math and physics, f.ex in integrals and sums. That's because they were historically done on paper, where you have a 2D canvas, so you can do stuff like this easily:</p> <div class="codehilite"><pre><span></span><code>10 Σ n n=0 </code></pre></div> <p>We express the ternary by putting arguments above and below the operator. All mainstream programming languages are linear, though, so any given symbol has only two sides. Plus functions are more regular and universal than infix operators so you might as well write <code>Sum(n=0, 10, n)</code>. The conditional ternary slips through purely because it's just so darn useful. Though now I'm wondering where it comes from in the first place. Different newsletter, maybe.</p> <p>But I still find ternary operators super interesting, please let me know if you know any I haven't covered!</p> <hr/> <h3>Blog Rec</h3> <p>This week's blog rec is <a href="https://lexi-lambda.github.io/" target="_blank">Alexis King</a>! Generally, Alexis's work spans the theory, practice, and implementation of programming languages, aimed at a popular audience and not an academic one. If you know her for one thing, it's probably <a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate" target="_blank">Parse, don't validate</a>, which is now so mainstream most people haven't read the original post. Another good one is about <a href="https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/" target="_blank">modeling open-world systems with static types</a>. </p> <p>Nowadays she is <em>far</em> more active on <a href="https://langdev.stackexchange.com/users/861/alexis-king" target="_blank">Programming Languages Stack Exchange</a>, where she has blog-length answers on <a href="https://langdev.stackexchange.com/questions/2692/how-should-i-read-type-system-notation/2693#2693" target="_blank">reading type notations</a>, <a href="https://langdev.stackexchange.com/questions/3942/what-are-the-ways-compilers-recognize-complex-patterns/3945#3945" target="_blank">compiler design</a>, and <a href="https://langdev.stackexchange.com/questions/2069/what-is-an-arrow-and-what-powers-would-it-give-as-a-first-class-concept-in-a-pro/2372#2372" target="_blank">why arrows</a>.</p> <div class="footnote"> <hr/> <ol> <li id="fn:lisp"> <p>Unless it's a lisp. <a class="footnote-backref" href="#fnref:lisp" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:ternary"> <p>Or <code>x if bool else y</code>, same thing. <a class="footnote-backref" href="#fnref:ternary" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:ternaryish"> <p>I say "ish" because trains can be arbitrarily long: <code>x (f1 f2 f3 f4 f5) y</code> is something I have <em>no idea</em> <a href="https://code.jsoftware.com/wiki/Vocabulary/fork" target="_blank">how to parse</a>. <a class="footnote-backref" href="#fnref:ternaryish" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 05 Nov 2024 18:40:33 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/</guid></item><item><title>TLA from first principles</title><link>https://buttondown.com/hillelwayne/archive/tla-from-first-principles/</link><description><h3>No Newsletter next week</h3> <p>I'll be speaking at <a href="https://www.usenix.org/conference/srecon24emea/presentation/wayne" target="_blank">USENIX SRECon</a>!</p> <h2>TLA from first principles</h2> <p>I'm working on v0.5 of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>. In the process of revising the "System Modeling" chapter, I stumbled on a great way to explain the <strong>T</strong>emporal <strong>L</strong>ogic of <strong>A</strong>ctions that TLA+ is based on. I'm reproducing that bit here with some changes to fit the newsletter format.</p> <p>Note that by this point the reader has already encountered property testing, formal verification, decision tables, and nontemporal specifications, and should already have a lot of practice expressing things as predicates. </p> <hr/> <h3>The intro</h3> <p>We have some bank users, each with an account balance. Bank users can wire money to each other. We have overdraft protection, so wires cannot reduce an account value below zero. </p> <p>For the purposes of introducing the ideas, we'll assume an extremely simple system: two hardcoded variables <code>alice</code> and <code>bob</code>, both start with 10 dollars, and transfers are only from Alice to Bob. Also, the transfer is totally atomic: we check for adequate funds, withdraw, and deposit all in a single moment of time. Later [in the chapter] we'll allow for multiple nonatomic transfers at the same time.</p> <p>First, let's look at a valid <strong>behavior</strong> of the system, or possible way it can evolve.</p> <div class="codehilite"><pre><span></span><code>alice 10 -> 5 -> 3 -> 3 -> ... bob 10 -> 15 -> 17 -> 17 -> ... </code></pre></div> <p>In programming, we'd think of <code>alice</code> and <code>bob</code> as variables that change. How do we represent those variables <em>purely</em> in terms of predicate logic? One way is to instead think of them as <em>arrays</em> of values. <code>alice[0]</code> is the initial state of <code>alice</code>, <code>alice[1]</code> is after the first time step, etc. Time, then, is "just" the set of natural numbers.</p> <div class="codehilite"><pre><span></span><code>Time = {0, 1, 2, 3, ...} alice = [10, 5, 3, 3, ...] bob = [10, 15, 17, 17, ...] </code></pre></div> <p>In comparison to our valid behavior, here are some <em>invalid</em> behaviors:</p> <div class="codehilite"><pre><span></span><code>alice = [10, 3, ...] bob = [10 15, ...] alice = [10, -1, ...] bob = [10 21, ...] </code></pre></div> <p>The first is invalid because Bob received more money than Alice lost. The second is invalid because it violates our proposed invariant, that accounts cannot go negative. Can we write a predicate that is <em>true</em> for valid transitions and <em>false</em> for our two invalid behaviors?</p> <p>Here's one way:</p> <div class="codehilite"><pre><span></span><code>Time = Nat // {0, 1, 2, etc} Transfer(t: Time) = some value in 0..=alice[t]: 1. alice[t+1] = alice[t] - value 2. bob[t+1] = bob[t] + value </code></pre></div> <p>Go through and check that this is true for every <code>t</code> in the valid behavior and false for at least one <code>t</code> in the invalid behavior. Note that the steps where Alice <em>doesn't</em> send a transfer also pass <code>Transfer</code>; we just pick <code>value = 0</code>.</p> <p>I can now write a predicate that perfectly describes a valid behavior:</p> <div class="codehilite"><pre><span></span><code>Spec = 1. alice[0] = 10 2. bob[0] = 10 3. all t in Time: Transfer(t) </code></pre></div> <p>Now allowing "nothing happens" as "Alice sends an empty transfer" is a little bit weird. In the real system, we probably don't want people to constantly be sending each other zero dollars:</p> <div class="codehilite"><pre><span></span><code>Transfer(t: Time) = <span class="gd">- some value in 0..=alice[t]:</span> <span class="gi">+ some value in 1..=alice[t]:</span> <span class="w"> </span> 1. alice[t+1] = alice[t] - value <span class="w"> </span> 2. bob[t+1] = bob[t] + value </code></pre></div> <p>But now there can't be a timestep where nothing happens. And that means <em>no</em> behavior is valid! At every step, Alice <em>must</em> transfer at least one dollar to Bob. Eventually there is some <code>t</code> where <code>alice[t] = 0 && bob[t] = 20</code>. Then Alice can't make a transfer, <code>Transfer(t)</code> is false, and so <code>Spec</code> is false.<sup id="fnref:exercise"><a class="footnote-ref" href="#fn:exercise">1</a></sup></p> <p>So typically when modeling we add a <strong>stutter step</strong>, like this:</p> <div class="codehilite"><pre><span></span><code>Spec = 1. alice[0] = 10 2. bob[0] = 10 3. all t in Time: || Transfer(t) || 1. alice[t+1] = alice[t] 2. bob[t+1] = bob[t] </code></pre></div> <p>(This is also why we can use infinite behaviors to model a finite algorithm. If the algorithm completes at <code>t=21</code>, <code>t=22,23,24...</code> are all stutter steps.)</p> <p>There's enough moving parts here that I'd want to break it into subpredicates.</p> <div class="codehilite"><pre><span></span><code>Init = 1. alice[0] = 10 2. bob[0] = 10 Stutter(t) = 1. alice[t+1] = alice[t] 2. bob[t+1] = bob[t] Next(t) = Transfer(t) // foreshadowing Spec = 1. Init 2. all t in Time: Next(t) || Stutter(t) </code></pre></div> <p>Now finally, how do we represent the property <code>NoOverdrafts</code>? It's an <em>invariant</em> that has to be true at all times. So we do the same thing we did in <code>Spec</code>, write a predicate over all times.</p> <div class="codehilite"><pre><span></span><code>property NoOverdrafts = all t in Time: alice[t] >= 0 && bob[t] >= 0 </code></pre></div> <p>We can even say that <code>Spec => NoOverdrafts</code>, ie if a behavior is valid under <code>Spec</code>, it satisfies <code>NoOverdrafts</code>.</p> <h4>One of the exercises</h4> <p>Modify the <code>Next</code> so that Bob can send Alice transfers, too. Don't try to be too clever, just do this in the most direct way possible.</p> <p>Bonus: can Alice and Bob transfer to each other in the same step?</p> <p><strong>Solution</strong> [in back of book]: We can rename <code>Transfer(t)</code> to <code>TransferAliceToBob(t)</code>, write the converse as a new predicate, and then add it to <code>next</code>. Like this</p> <div class="codehilite"><pre><span></span><code>TransferBobToAlice(t: Time) = some value in 1..=bob[t]: 1. alice[t+1] = alice[t] - value 2. bob[t+1] = bob[t] + value Next(t) = || TransferAliceToBob(t) || TransferBobToAlice(t) </code></pre></div> <p>Now, can Alice and Bob transfer to each other in the same step? No. Let's say they both start with 10 dollars and each try to transfer five dollars to each other. By <code>TransferAliceToBob</code> we have:</p> <div class="codehilite"><pre><span></span><code>1. alice[1] = alice[0] - 5 = 5 2. bob[1] = bob[0] + 5 = 15 </code></pre></div> <p>And by <code>TransferBobToAlice</code>, we have:</p> <div class="codehilite"><pre><span></span><code>1. bob[1] = bob[0] - 5 = 5 2. alice[1] = alice[0] + 5 = 15 </code></pre></div> <p>So now we have <code>alice[1] = 5 && alice[1] = 15</code>, which is always false.</p> <h3>Temporal Logic</h3> <div class="subscribe-form"></div> <p>This is good and all, but in practice, there's two downsides to treating time as a set we can quantify over:</p> <ol> <li>It's cumbersome. We have to write <code>var[t]</code> and <code>var[t+1]</code> all over the place.</li> <li>It's too powerful. We can write expressions like <code>alice[t^2-5] = alice[t] + t</code>.</li> </ol> <p>Problem (2) might seem like a good thing; isn't the whole <em>point</em> of logic to be expressive? But we have a long-term goal in mind: getting a computer to check our formal specification. We need to limit the expressivity of our model so that we can make it checkable. </p> <p>In practice, this will mean making time implicit to our model, instead of explicitly quantifying over it.</p> <p>The first thing we need to do is limit how we can use time. At a given point in time, all we can look at is the <em>current</em> value of a variable (<code>var[t]</code>) and the <em>next</em> value (<code>var[t+1]</code>). No <code>var[t+16]</code> or <code>var[t-1]</code> or anything else complicated.</p> <p>And it turns out we've already seen a mathematical convention for expressing this: <strong>priming</strong>!<sup id="fnref:priming"><a class="footnote-ref" href="#fn:priming">2</a></sup> For a given time <code>t</code>, we can define <code>var</code> to mean <code>var[t]</code> and <code>var'</code> to mean <code>var[t+1]</code>. Then <code>Transfer(t)</code> becomes</p> <div class="codehilite"><pre><span></span><code>Transfer = some value in 1..=alice: 1. alice' = alice 2. bob' = bob </code></pre></div> <p>Next we have the construct <code>all t in Time: P(t)</code> in both <code>Spec</code> and <code>NoOverdrafts</code>. In other words, "P is always true". So we can add <code>always</code> as a new term. Logicians conventionally use □ or <code>[]</code> to mean the same thing.<sup id="fnref:beyond"><a class="footnote-ref" href="#fn:beyond">3</a></sup></p> <div class="codehilite"><pre><span></span><code>property NoOverdrafts = always (alice >= 0 && bob[t] >= 0) // or [](alice >= 0 && bob[t] >= 0) Spec = Init && always (Next || Stutter) </code></pre></div> <p>Now time is <em>almost</em> completely implicit in our spec, with just one exception: <code>Init</code> has <code>alice[0]</code> and <code>bob[0]</code>. We just need one more convention: if a variable is referenced <em>outside</em> of the scope of a temporal operator, it means <code>var[0]</code>. Since <code>Init</code> is outside of the <code>[]</code>, it becomes</p> <div class="codehilite"><pre><span></span><code>Init = 1. alice = 10 2. bob = 10 </code></pre></div> <p>And with that, we've removed <code>Time</code> as an explicit value in our model.</p> <p>The addition of primes and <code>always</code> makes this a <strong>temporal logic</strong>, one that can model how things change over time. And that makes it ideal for modeling software systems.</p> <h3>Modeling with TLA+</h3> <p>One of the most popular specification languages for modeling these kinds of concurrent systems is <strong>TLA+</strong>. TLA+ was invented by the Turing award-winner Leslie Lamport, who also invented a wide variety of concurrency algorithms and LaTeX. Here's our current spec in TLA+:</p> <div class="codehilite"><pre><span></span><code>---- MODULE transfers ---- EXTENDS Integers VARIABLES alice, bob vars == <<alice, bob>> Init == alice = 10 /\ bob = 10 AliceToBob == \E amnt \in 1..alice: alice' = alice - amnt /\ bob' = bob + amnt BobToAlice == \E amnt \in 1..bob: alice' = alice + amnt /\ bob' = bob - amnt Next == AliceToBob \/ BobToAlice Spec == Init /\ [][Next]_vars NoOverdrafts == [](alice >= 0 /\ bob >= 0) ==== </code></pre></div> <p>TLA+ uses ASCII versions of mathematicians notation: <code>/\</code>/<code>\/</code> for <code>&&/||</code>, <code>\A \E</code> for <code>all/some</code>, etc. The only thing that's "unusual" (besides <code>==</code> for definition) is the <code>[][Next]_vars</code> bit. That's TLA+ notation for <code>[](Next || Stutter)</code>: <code>Next</code> or <code>Stutter</code> always happens.</p> <hr/> <p>The rest of the chapter goes on to explain model checking, PlusCal (for modeling nonatomic transactions without needing to explain the exotic TLA+ function syntax), and liveness properties. But this is the intuition behind the "temporal logic of actions": temporal operators are operations on the set of points of time, and we restrict what we can do with those operators to make reasoning about the specification feasible.</p> <p>Honestly I like it enough that I'm thinking of redesigning my TLA+ workshop to start with this explanation. Then again, maybe it only seems good to me because I already know TLA+. Please let me know what you think about it!</p> <p>Anyway, the new version of the chapter will be in v0.5, which should be out mid-November.</p> <hr/> <h3>Blog Rec</h3> <p>This one it's really dear to me: <a href="https://muratbuffalo.blogspot.com/" target="_blank">Metadata</a>, by Murat Demirbas. When I was first trying to learn TLA+ back in 2016, his post <a href="https://muratbuffalo.blogspot.com/2015/01/my-experience-with-using-tla-in.html" target="_blank">on using TLA+ in a distributed systems class</a> was one of, like... <em>three</em> public posts on TLA+. I must have spent hours rereading that post and puzzling out this weird language I stumbled into. Later I emailed Murat with some questions and he was super nice in answering them. Don't think I would have ever grokked TLA+ without him.</p> <p>In addition to TLA+ content, a lot of the blog is also breakdowns of papers he read— like <a href="https://blog.acolyer.org/" target="_blank">the morning paper</a>, except with a focus on distributed systems (and still active). If you're interested in learning more about the science of distributed systems, he has an excellent page on <a href="https://muratbuffalo.blogspot.com/2021/02/foundational-distributed-systems-papers.html" target="_blank">foundational distributed systems papers</a>. But definitely check out his <a href="https://muratbuffalo.blogspot.com/2023/09/metastable-failures-in-wild.html" target="_blank">his deep readings</a>, too!</p> <div class="footnote"> <hr/> <ol> <li id="fn:exercise"> <p>In the book this is presented as an exercise (with the solution in back). The exercise also clarifies that since <code>Time = Nat</code>, all behaviors have an <em>infinite</em> number of steps. <a class="footnote-backref" href="#fnref:exercise" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:priming"> <p>Priming is introduced in the chapter on decision tables, and again in the chapter on database invariants. <code>x'</code> is "the next value of <code>x</code>", so you can use it to express database invariants like "jobs only move from <code>ready</code> to <code>started</code> or <code>aborted</code>." <a class="footnote-backref" href="#fnref:priming" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:beyond"> <p>I'm still vacillating on whether I want a "beyond logic" appendix that covers higher order logic, constructive logic, and modal logic (which is what we're sneakily doing right now!)</p> <p>While I'm here, this explanation of <code>always</code> as <code>all t in Time</code> isn't <em>100%</em> accurate, since it doesn't explain why things like <code>[](P => []Q)</code> or <code><>[]P</code> make sense. But it's accurate in most cases and is a great intuition pump. <a class="footnote-backref" href="#fnref:beyond" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 22 Oct 2024 17:14:21 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/tla-from-first-principles/</guid></item><item><title>Be Suspicious of Success</title><link>https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/</link><description><p>From Leslie Lamport's <em>Specifying Systems</em>:</p> <blockquote> <p>You should be suspicious if [the model checker] does not find a violation of a liveness property... you should also be suspicious if [it] finds no errors when checking safety properties. </p> </blockquote> <p>This is specifically in the context of model-checking a formal specification, but it's a widely applicable software principle. It's not enough for a program to work, it has to work for the <em>right reasons</em>. Code working for the wrong reasons is code that's going to break when you least expect it. And since "correct for right reasons" is a much narrower target than "correct for any possible reason", we can't assume our first success is actually our intended success.</p> <p>Hence, BSOS: <strong>Be Suspicious of Success</strong>.</p> <h3>Some useful BSOS practices</h3> <p>The standard way of dealing with BSOS is verification. Tests, static checks, model checking, etc. We get more confident in our code if our verifications succeed. But then we also have to be suspicious of <em>that</em> success, too! How do I know whether my tests are passing because they're properly testing correct code or because they're failing to test incorrect code?</p> <p>This is why test-driven development gurus tell people to write a failing test first. Then at least we know the tests are doing <em>something</em> (even if they still might not be testing what they want).</p> <p>The other limit of verification is that it can't tell us <em>why</em> something succeeds. Mainstream verification methods are good at explaining why things <em>fail</em>— expected vs actual test output, type mismatches, specification error traces. Success isn't as "information-rich" as failure. How do you distinguish a faithful implementation of <a href="https://en.wikipedia.org/wiki/Collatz_conjecture" target="_blank"><code>is_collatz_counterexample</code></a> from <code>return false</code>?</p> <p>A broader technique I follow is <em>make it work, make it break</em>. If code is working for the right reasons, I should be able to predict how to break it. This can be either a change in the runtime (this will livelock if we 10x the number of connections), or a change to the code itself (commenting out <em>this</em> line will cause property X to fail). <sup id="fnref:superproperties"><a class="footnote-ref" href="#fn:superproperties">1</a></sup> If the code still works even after the change, my model of the code is wrong and it was succeeding for the wrong reasons.</p> <h3>Happy and Sad Paths</h3> <div class="subscribe-form"></div> <p>A related topic (possibly subset?) is "happy and sad paths". The happy path of your code is the behavior when everything's going right: correct inputs, preconditions are satisfied, the data sources are present, etc. The sad path is all of the code that handles things going wrong. Retry mechanisms, insufficient user authority, database constraint violation, etc. In most software, the code supporting the sad paths dwarfs the code in the happy path.</p> <p>BSOS says that I can't just show code works in the happy path, I also need to check it works in the sad path. </p> <p>BSOS also says that I have to be suspicious when the sad path works properly, too. </p> <p>Say I add a retry mechanism to my code to handle the failure mode of timeouts. I test the code and it works. Did the retry code actually <em>run</em>? Did it run <em>regardless</em> of the original response? Is it really doing exponential backoff? Will stop after the maximum retry limit? Is the sad path code <em>after</em> the maximum retry limit working properly?</p> <p><a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf" target="_blank">One paper</a> found that 35% of catastrophic distributed system failures were caused by "trivial mistakes in error handlers" (pg 9). These were in mature, battle-hardened programs. Be suspicious of success. Be more suspicious of sad path success.</p> <hr/> <h2>Blog Rec</h2> <p>This week's blog rec is <a href="https://www.redblobgames.com/" target="_blank">Red Blob Games</a>!<sup id="fnref:blogs-vs-articles"><a class="footnote-ref" href="#fn:blogs-vs-articles">2</a></sup> While primarily about computer game programming, the meat of the content is beautiful, interactive guides to general CS algorithms. Some highlights:</p> <ul> <li><a href="https://www.redblobgames.com/pathfinding/a-star/introduction.html" target="_blank">Introduction to the A* Algorithm</a> was really illuminating when I was a baby programmer.</li> <li>I'm sure this <a href="https://www.redblobgames.com/articles/noise/introduction.html" target="_blank">overview of noise functions</a> will be useful to me <em>someday</em>. Maybe for test data generation?</li> <li>If you're also an explainer type he has a lot of great stuff on <a href="https://www.redblobgames.com/making-of/line-drawing/" target="_blank">his process</a> and his <a href="https://www.redblobgames.com/making-of/little-things/" target="_blank">little tricks</a> to make things more understandable.</li> </ul> <p>(I don't think his <a href="https://www.redblobgames.com/blog/posts.xml" target="_blank">rss feed</a> covers new interactive articles, only the <a href="https://www.redblobgames.com/blog/" target="_blank">blog</a> specifically.)</p> <div class="footnote"> <hr/> <ol> <li id="fn:superproperties"> <p><a href="https://www.jameskoppel.com/" target="_blank">Jimmy Koppel</a> once proposed that just as code has properties, code variations have <a href="https://groups.csail.mit.edu/sdg/pubs/2020/demystifying_dependence_published.pdf" target="_blank"><strong>superproperties</strong></a>. For example, "no modification to the codebase causes us to use a greater number of deprecated APIs." <a class="footnote-backref" href="#fnref:superproperties" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:blogs-vs-articles"> <p>Okay, it's more an <em>article</em> site, because there's also a <a href="https://www.redblobgames.com/blog/" target="_blank">Red Blob <em>blog</em></a> (which covers a lot of neat stuff, too). Maybe I should just rename this section to "site rec". <a class="footnote-backref" href="#fnref:blogs-vs-articles" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 16 Oct 2024 15:08:39 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/</guid></item><item><title>How to convince engineers that formal methods is cool</title><link>https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/</link><description><p>Sorry there was no newsletter last week! I got COVID. Still got it, which is why this one's also short.</p> <h3>Logic for Programmers v0.4</h3> <p><a href="https://leanpub.com/logic/" target="_blank">Now available</a>! This version adds a chapter on TLA+, significantly expands the constraint solver chapter, and adds a "planner programming" section to the Logic Programming chapter. You can see the full release notes on the <a href="https://leanpub.com/logic/" target="_blank">book page</a>.</p> <h1>How to convince engineers that formal methods is cool</h1> <p>I have an open email for answering questions about formal methods,<sup id="fnref:fs-fv"><a class="footnote-ref" href="#fn:fs-fv">1</a></sup> and one of the most common questions I get is "how do I convince my coworkers that this is worth doing?" usually the context is the reader is really into the idea of FM but their coworkers don't know it exists. The goal of the asker is to both introduce FM and persuade them that FM's useful. </p> <p>In my experience as a consultant and advocate, I've found that there's only two consistently-effective ways to successfully pitch FM:</p> <ol> <li>Use FM to find an <em>existing</em> bug in a work system</li> <li>Show how FM finds a historical bug that's already been fixed.</li> </ol> <h4>Why this works</h4> <p>There's two main objections to FM that we need to address. The first is that FM is too academic and doesn't provide a tangible, practical benefit. The second is that FM is too hard; only PhDs and rocket scientists can economically use it. (Showing use cases from AWS <em>et al</em> aren't broadly persuasive because skeptics don't have any insight into how AWS functions.) Finding an existing bug hits both: it helped the team with a real problem, and it was done by a mere mortal. </p> <div class="subscribe-form"></div> <p>Demonstrating FM on a historical bug isn't <em>as</em> effective: it only shows that formal methods <em>could have</em> helped, not that it actually does help. But people will usually remember the misery of debugging that problem. Bug war stories are popular for a reason!</p> <h3>Making historical bugs persuasive</h3> <p>So "live bug" is a stronger rec, but "historical bug" tends to be easier to show. This is because <em>you know what you're looking for</em>. It's easier to write a high-level spec on a system you already know, and show it finds a bug you already know about.</p> <p>The trick to make it look convincing is to make the spec and bug as "natural" as possible. You can't make it seem like FM only found the bug because you had foreknowledge of what it was— then the whole exercise is too contrived. People will already know you had foreknowledge, of course, and are factoring that into their observations. You want to make the case that the spec you're writing is clear and obvious enough that an "ignorant" person could have written it. That means nothing contrived or suspicious.</p> <p>This is a bit of a fuzzy definition, more a vibe than anything. Ask yourself "does this spec look like something that was tailor-made around this bug, or does it find the bug as a byproduct of being a regular spec?"</p> <p>A good example of a "natural" spec is <a href="https://www.hillelwayne.com/post/augmenting-agile/" target="_blank">the bounded queue problem</a>. It's a straight translation of some Java code with no properties besides deadlock checking. Usually you'll be at a higher level of abstraction, though.</p> <hr/> <h3>Blog rec: <a href="https://www.argmin.net/" target="_blank">arg min</a></h3> <p>This is a new section I want to try for a bit: recommending tech(/-adjacent) blogs that I like. This first one is going to be a bit niche: <a href="https://www.argmin.net/" target="_blank">arg min</a> is writing up lecture notes on "convex optimization". It's a cool look into the theory behind constraint solving. I don't understand most of the math but the prose is pretty approachable. Couple of highlights:</p> <ul> <li><a href="https://www.argmin.net/p/modeling-dystopia" target="_blank">Modeling Dystopia</a> about why constraint solving isn't a mainstream technology.</li> <li><a href="https://www.argmin.net/p/convex-optimization-live-blog" target="_blank">Table of Contents</a> to see all of the posts.</li> </ul> <p>The blogger also talks about some other topics but I haven't read those posts much.</p> <div class="footnote"> <hr/> <ol> <li id="fn:fs-fv"> <p>As always, talking primarily about formal specification of systems (TLA+/Alloy/Spin), not formal verification of code (Dafny/SPARK/Agda). I talk about the differences a bit <a href="https://www.hillelwayne.com/post/why-dont-people-use-formal-methods/" target="_blank">here</a> (but I really need to write a more focused piece). <a class="footnote-backref" href="#fnref:fs-fv" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 08 Oct 2024 16:18:55 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/</guid></item><item><title>Refactoring Invariants</title><link>https://buttondown.com/hillelwayne/archive/refactoring-invariants/</link><description><p>(Feeling a little sick so this one will be short.)</p> <p>I'm often asked by clients to review their (usually TLA+) formal specifications. These specs are generally slower and more convoluted than an expert would write. I want to fix them up without changing the overall behavior of the spec or introducing subtle bugs.</p> <p>To do this, I use a rather lovely feature of TLA+. Say I see a 100-line <code>Foo</code> action that I think I can refactor down to 20 lines. I'll first write a refactored version as a separate action <code>NewFoo</code>, then I run the model checker with the property</p> <div class="codehilite"><pre><span></span><code>RefactorProp == [][Foo <=> NewFoo]_vars </code></pre></div> <p>That's an intimidating nest of symbols but all it's saying is that every <code>Foo</code> step must also be a <code>NewFoo</code> step. If the refactor ever does something different from the original action, the model-checker will report the exact behavior and transition it fails for. Conversely, if the model checker passes, I can safely assume they have identical behaviors.</p> <p>This is a <strong>refactoring invariant</strong>:<sup id="fnref:invariant"><a class="footnote-ref" href="#fn:invariant">1</a></sup> the old and new versions of functions have identical behavior. Refactoring invariants are superbly useful in formal specification. Software devs spend enough time refactoring that they'd be useful for coding, too.</p> <p>Alas, refactoring invariants are a little harder to express in code. In TLA+ we're working with bounded state spaces, so the model checker can check the invariant for every possible state. Even a simple program can have an unbounded state space via an infinite number of possible function inputs. </p> <p>(Also formal specifications are "pure" simulations while programs have side effects.)</p> <p>The "normal" way to verify a program refactoring is to start out with a huge suite of <a href="https://buttondown.com/hillelwayne/archive/oracle-testing/" target="_blank">oracle tests</a>. This <em>should</em> catch a bad refactor via failing tests. The downside is that you might not have the test suite in the first place, or not one that covers your particular refactoring. Second, even if the test suite does, it only indirectly tests the invariant. It catches the refactoring error as a consequence of testing other stuff. What if we want to directly test the refactoring invariant?</p> <h3>Two ways of doing this</h3> <p>One: by pulling in formal methods. Ray Myers has a <a href="https://www.youtube.com/watch?v=UdB3XBf219Y" target="_blank">neat video</a> on formally proving a refactoring is correct. That one's in the niche language ACL2, but he's also got one on <a href="https://www.youtube.com/watch?v=_7RXQE-pCMo" target="_blank">refactoring C</a>. You might not even to prove the refactoring correct, you could probably get away with using an <a href="https://github.com/pschanely/CrossHair" target="_blank">SMT solver</a> to find counterexamples.</p> <p>Two: by using property-based testing. Generate random inputs, pass them to both functions, and check that the outputs are identical. Using the python <a href="https://hypothesis.readthedocs.io/en/latest/" target="_blank">Hypothesis</a> library:</p> <div class="codehilite"><pre><span></span><code><span class="kn">from</span> <span class="nn">hypothesis</span> <span class="kn">import</span> <span class="n">given</span> <span class="kn">import</span> <span class="nn">hypothesis.strategies</span> <span class="k">as</span> <span class="nn">st</span> <span class="c1"># from the `gilded rose kata`</span> <span class="k">def</span> <span class="nf">update_quality</span><span class="p">(</span><span class="nb">list</span><span class="p">[</span><span class="n">Item</span><span class="p">]):</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">update_quality_new</span><span class="p">(</span><span class="nb">list</span><span class="p">[</span><span class="n">Item</span><span class="p">]):</span> <span class="o">...</span> <span class="nd">@given</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">lists</span><span class="p">(</span><span class="n">st</span><span class="o">.</span><span class="n">builds</span><span class="p">(</span><span class="n">Item</span><span class="p">)))</span> <span class="k">def</span> <span class="nf">test_refactoring</span><span class="p">(</span><span class="n">l</span><span class="p">):</span> <span class="k">assert</span> <span class="n">update_quality</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> <span class="o">==</span> <span class="n">update_quality_new</span><span class="p">(</span><span class="n">l</span><span class="p">)</span> </code></pre></div> <p>One tricky bit is if the function is part of a long call chain <code>A -> B -> C</code>, and you want to test that refactoring <code>C'</code> doesn't change the behavior of <code>A</code>. You'd have to add a <code>B'</code> that uses <code>C'</code> and then an <code>A'</code> that uses <code>B'</code>. Maybe you could instead create a branch, commit the change the <code>C'</code> in that branch, and then run a <a href="https://www.hillelwayne.com/post/cross-branch-testing/" target="_blank">cross-branch test</a> against each branch's <code>A</code>.</p> <p>Impure functions are harder. The test now makes some side effect twice, which could spuriously break the refactoring invariant. You could instead test the changes are the same, or try to get the functions to effect different entities and then compare the updates of each entity. There's no general solution here though, and there might be No Good Way for a particular effectful refactoring.</p> <h3>Behavior-changing rewrites</h3> <p>We can apply similar ideas for rewrites that change <em>behavior</em>. Say we have an API, and v1 returns a list of user names while v2 returns a <code>{version, userids}</code> dict. Then we can find some transformation of v2 into v1, and run the refactoring invariant on that:</p> <div class="codehilite"><pre><span></span><code><span class="k">def</span> <span class="nf">v2_to_v1</span><span class="p">(</span><span class="n">v2_resp</span><span class="p">):</span> <span class="k">return</span> <span class="p">[</span><span class="n">User</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="o">.</span><span class="n">name</span> <span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">v2_resp</span><span class="p">[</span><span class="s2">"userids"</span><span class="p">]]</span> <span class="nd">@given</span><span class="p">(</span><span class="n">some_query_generator</span><span class="p">)</span> <span class="k">def</span> <span class="nf">test_refactoring</span><span class="p">(</span><span class="n">q</span><span class="p">):</span> <span class="k">assert</span> <span class="n">v1</span><span class="p">(</span><span class="n">q</span><span class="p">)</span> <span class="o">==</span> <span class="n">v2_to_v1</span><span class="p">(</span><span class="n">v2</span><span class="p">(</span><span class="n">q</span><span class="p">))</span> </code></pre></div> <p>Fun fact: <code>v2_to_v1</code> is a <a href="https://buttondown.com/hillelwayne/archive/software-isomorphisms/" target="_blank">software homomorphism</a>!</p> <div class="footnote"> <hr/> <ol> <li id="fn:invariant"> <p>Well technically it's an <em>action property</em> since it's on the transitions of states, not the states, but "refactor invariant" gets the idea across better. <a class="footnote-backref" href="#fnref:invariant" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 24 Sep 2024 20:06:10 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/refactoring-invariants/</guid></item><item><title>Goodhart's Law in Software Engineering</title><link>https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/</link><description><h3>Blog Hiatus</h3> <p>You might have noticed I haven't been updating my website. I haven't even <em>looked</em> at any of my drafts for the past three months. All that time is instead going into <em>Logic for Programmers</em>. I'll get back to the site when that's done or in 2025, whichever comes first. Newsletter and <a href="https://www.patreon.com/hillelwayne" target="_blank">Patreon</a> will still get regular updates.</p> <p>(As a comparison, the book is now 22k words. That's like 11 blog posts!)</p> <h2>Goodhart's Law in Software Engineering</h2> <p>I recently got into an argument with some people about whether small functions were <em>mostly</em> a good idea or <em>always 100%</em> a good idea, and it reminded me a lot about <a href="https://en.wikipedia.org/wiki/Goodhart%27s_law" target="_blank">Goodhart's Law</a>:</p> <blockquote> <p>When a measure becomes a target, it ceases to be a good measure.</p> </blockquote> <p>The <em>weak</em> version of this is that people have perverse incentives to game the metrics. If your metric is "number of bugs in the bug tracker", people will start spuriously closing bugs just to get the number down. </p> <p>The <em>strong</em> version of the law is that even 100% honest pursuit of a metric, taken far enough, is harmful to your goals, and this is an inescapable consequence of the difference between metrics and values. We have metrics in the first place because what we actually <em>care about</em> is nonquantifiable. There's some <em>thing</em> we want more of, but we have no way of directly measuring that thing. We <em>can</em> measure something that looks like a rough approximation for our goal. But it's <em>not</em> our goal, and if we replace the metric with the goal, we start taking actions that favor the metric over the goal.</p> <p>Say we want more reliable software. How do you measure "reliability"? You can't. But you <em>can</em> measure the number of bugs in the bug tracker, because fewer open bugs roughly means more reliability. <strong>This is not the same thing</strong>. I've seen bugs fixed in ways that made the system <em>less</em> reliable, but not in ways that translated into tracked bugs.</p> <p>I am a firm believer in the strong version of Goodhart's law. Mostly because of this:</p> <p><img alt="A peacock with its feathers out. The peacock is scremming" class="newsletter-image" src="https://assets.buttondown.email/images/2573503d-bc57-49ce-aa26-9d399d801118.jpg?w=960&fit=max"/></p> <p>What does a peahen look for in a mate? A male with maximum fitness. What's a metric that approximates fitness? How nice the plumage is, because nicer plumage = more calories energy to waste on plumage.<sup id="fnref:peacock"><a class="footnote-ref" href="#fn:peacock">1</a></sup> But that only <em>approximates</em> fitness, and over generations the plumage itself becomes the point at the cost of overall bird fitness. Sexual selection is Goodhart's law in action.</p> <div class="subscribe-form"></div> <p>If the blind watchmaker can fall for Goodhart, people can too.</p> <h3>Examples in Engineering</h3> <p>Goodhart's law is a warning for pointy-haired bosses who up with terrible metrics: lines added, feature points done, etc. I'm more interested in how it affects the metrics we set for ourselves that our bosses might never know about.</p> <ul> <li>"Test coverage" is a proxy for how thoroughly we've tested our software. It diverges when we need to test lots of properties of the same lines of code, or when our worst bugs are emergent at the integration level.</li> <li>"Cyclomatic complexity" and "function size" are proxies for code legibility. They diverges when we think about global module legibility, not local function legibility. Then too many functions can obscure the code and data flow.</li> <li>Benchmarks are proxies for performant programs, and diverge when improving benchmarks slows down unbenchmarked operations.</li> <li>Amount of time spent pairing/code reviewing/debugging/whatever proxies "being productive".</li> <li><a href="https://dora.dev/" target="_blank">The DORA report</a> is an interesting case, because it claims four metrics<sup id="fnref:metrics"><a class="footnote-ref" href="#fn:metrics">2</a></sup> are proxies to ineffable goals like "elite performance" and <em>employee satisfaction</em>. It also argues that you should minimize commit size to improve the DORA metrics. A proxy of a proxy of a goal!</li> </ul> <h3>What can we do about this?</h3> <p>No, I do not know how to avoid a law that can hijack the process of evolution.</p> <p>The 2023 DORA report suggests readers should avoid Goodhart's law and "assess a team's strength across a wide range of people, processes, and technical capabilities" (pg 10), which is kind of like saying the fix to production bugs is "don't write bugs". It's a guiding principle but not actionable advice that gets to that principle.</p> <p>They also say "to use a combination of metrics to drive deeper understanding" (ibid), which makes more sense at first. If you have metrics X and Y to approximate goal G, then overoptimizing X <em>might</em> hurt Y, indicating you're getting further from G. In practice I've seen it turn into "we can't improve X because it'll hurt Y and we can't improve Y because it'll hurt X." This <em>could</em> mean we're at the best possible spot for G, but more often it means we're trapped very far from our goal. You could come up with a weighted combination of X and Y, like 0.7X + 0.3Y, but <em>that too</em> is a metric subject to Goodhart. </p> <p>I guess the best I can do is say "use your best engineering judgement"? Evolution is mindless, people aren't. Again, not an actionable or scalable bit of advice, but as I grow older I keep finding "use your best judgement" is all we can do. Knowledge work is ineffable and irreducible.</p> <div class="footnote"> <hr/> <ol> <li id="fn:peacock"> <p>This sent me down a rabbit hole; turns out scientists are still debating what <em>exactly</em> the peacock's tail is used for! Is it sexual selection? Adverse signalling? Something else??? <a class="footnote-backref" href="#fnref:peacock" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:metrics"> <p>How soon commits get to production, deployment frequency, percent of deployments that cause errors in production, and mean time to recovery. <a class="footnote-backref" href="#fnref:metrics" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 17 Sep 2024 16:33:40 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/</guid></item><item><title>Why Not Comments</title><link>https://buttondown.com/hillelwayne/archive/why-not-comments/</link><description><h2>Logic For Programmers v0.3</h2> <p><a href="https://leanpub.com/logic/" target="_blank">Now available</a>! It's a light release as I learn more about formatting a nice-looking book. You can see some of the differences between v2 and v3 <a href="https://bsky.app/profile/hillelwayne.com/post/3l3egdqnqj62o" target="_blank">here</a>.</p> <h2>Why Not Comments</h2> <p>Code is written in a structured machine language, comments are written in an expressive human language. The "human language" bit makes comments more expressive and communicative than code. Code has a limited amount of something <em>like</em> human language contained in identifiers. "Comment the why, not the what" means to push as much information as possible into identifiers. <a href="https://buttondown.com/hillelwayne/archive/3866bd6e-22c3-4098-92ef-4d47ef287ed8" target="_blank">Not all "what" can be embedded like this</a>, but a lot can.</p> <p>In recent years I see more people arguing that <em>whys</em> do not belong in comments either, that they can be embedded into <code>LongFunctionNames</code> or the names of test cases. Virtually all "self-documenting" codebases add documentation through the addition of identifiers.<sup id="fnref:exception"><a class="footnote-ref" href="#fn:exception">1</a></sup></p> <p>So what's something in the range of human expression that <em>cannot</em> be represented with more code?</p> <p>Negative information, drawing attention to what's <em>not</em> there. The "why nots" of the system.</p> <h3>A Recent Example</h3> <p>This one comes from <em>Logic for Programmers</em>. For convoluted technical reasons the epub build wasn't translating math notation (<code>\forall</code>) into symbols (<code>∀</code>). I wrote a script to manually go through and replace tokens in math strings with unicode equivalents. The easiest way to do this is to call <code>string = string.replace(old, new)</code> for each one of the 16 math symbols I need to replace (some math strings have multiple symbols).</p> <p>This is incredibly inefficient and I could instead do all 16 replacements in a single pass. But that would be a more complicated solution. So I did the simple way with a comment:</p> <div class="codehilite"><pre><span></span><code>Does 16 passes over each string BUT there are only 25 math strings in the book so far and most are <5 characters. So it's still fast enough. </code></pre></div> <p>You can think of this as a "why I'm using slow code", but you can also think of it as "why not fast code". It's calling attention to something that's <em>not there</em>.</p> <h3>Why the comment</h3> <p>If the slow code isn't causing any problems, why have a comment at all?</p> <div class="subscribe-form"></div> <p>Well first of all the code might be a problem later. If a future version of <em>LfP</em> has hundreds of math strings instead of a couple dozen then this build step will bottleneck the whole build. Good to lay a signpost now so I know exactly what to fix later.</p> <p>But even if the code is fine forever, the comment still does something important: it shows <em>I'm aware of the tradeoff</em>. Say I come back to my project two years from now, open <code>epub_math_fixer.py</code> and see my terrible slow code. I ask "why did I write something so terrible?" Was it inexperience, time crunch, or just a random mistake?</p> <p>The negative comment tells me that I <em>knew</em> this was slow code, looked into the alternatives, and decided against optimizing. I don't have to spend a bunch of time reinvestigating only to come to the same conclusion. </p> <h2>Why this can't be self-documented</h2> <p>When I was first playing with this idea, someone told me that my negative comment isn't necessary, just name the function <code>RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffs</code>. Aside from the issues of being long, not explaining the tradeoffs, and that I'd have to change it everywhere if I ever optimize the code... This would make the code <em>less</em> self-documenting. It doesn't tell you what the function actually <em>does</em>.</p> <p>The core problem is that function and variable identifiers can only contain one clause of information. I can't store "what the function does" and "what tradeoffs it makes" in the same identifier. </p> <p>What about replacing the comment with a test. I guess you could make a test that greps for math blocks in the book and fails if there's more than 80? But that's not testing <code>EpubMathFixer</code> directly. There's nothing in the function itself you can hook into. </p> <p>That's the fundamental problem with self-documenting negative information. "Self-documentation" rides along with written code, and so describes what the code is doing. Negative information is about what the code is <em>not</em> doing. </p> <h3>End of newsletter speculation</h3> <p>I wonder if you can think of "why not" comments as a case of counterfactuals. If so, are "abstractions of human communication" impossible to self-document in general? Can you self-document an analogy? Uncertainty? An ethical claim?</p> <div class="footnote"> <hr/> <ol> <li id="fn:exception"> <p>One interesting exception someone told me: they make code "more self-documenting" by turning comments into <em>logging</em>. I encouraged them to write it up as a blog post but so far they haven't. If they ever do I will link it here. <a class="footnote-backref" href="#fnref:exception" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 10 Sep 2024 19:40:29 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/why-not-comments/</guid></item><item><title>Thoughts on "The Future of TLA+"</title><link>https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/</link><description><p>Last week Leslie Lamport posted <a href="https://lamport.azurewebsites.net/tla/future.pdf" target="_blank">The Future of TLA+</a>, saying "the future of TLA+ is in the hands of the TLA+ foundation". Lamport released TLA+ in 1999 and shepherded its development for the past 25 years. Transferring ownership of TLA+ to the official foundation has been in the works for a while now, and now it's time.</p> <p>In the document, Lamport also talks about some of his hopes for how the foundation will think about evolving TLA+. He gives three possible costs of any change:</p> <ol> <li>Breaking existing specifications</li> <li>Complicating the language. "Simplicity is a major goal of TLA+". </li> <li>Complicating the tooling. "There are few people contributing to the maintenance of the tools."</li> </ol> <p>With this, Lamport proposes removing several features, like octal numbers, submodules and tuple quantification.<sup id="fnref:tuple-quantification"><a class="footnote-ref" href="#fn:tuple-quantification">1</a></sup></p> <p>I would be sad if we lost tuple quantification, less so if we lost octal numbers. But what could be <em>added</em> to make TLA+ better? Features that don't just "save a few keystrokes" but significantly prove the expressiveness of our specifications.</p> <p>Some disclaimers. First, where I'm coming from: most of my contracts are either "get beginners started with TLA+" or "help a company write an exotic spec at the limit of what TLA+ can do." So I'm always thinking about how to teach the language better and how to abuse its most obscure features. Did you know about <a href="https://lamport.azurewebsites.net/tla/tla2-guide.pdf" target="_blank">labeled subexpressions</a>? You can do <em>magic</em> with labeled subexpressions.</p> <p>So I both want the skill floor lowered and the skill ceiling raised. This is often a contradictory goal.</p> <p>Second, these aren't official proposals or anything. I'm just sharing what I'd like to see, given infinite resources and no concerns about tradeoffs.</p> <p>Finally, most of these aren't changes to TLA+. They're for TLC.</p> <h3>TLA+ vs TLC</h3> <p>TLA+ is the specification language. TLC is the model checker used to check that specs written in that language satisfy certain properties. It's not the only tool; you could use <a href="https://proofs.tlapl.us/doc/web/content/Home.html" target="_blank">TLAPS</a> or <a href="https://apalache-mc.org/" target="_blank">Apalache</a> instead. But TLC is the most mature tool and the only one bundled with the standard distribution.</p> <p>This is a critical distinction for two reasons. One, TLC can only model-check a subset of TLA+. There are a set of four "forbidden operators" that cannot be used in any capacity (though the lead TLC developer [Markus Kuppe] is working on adding one). Lamport lists them in section 3.3 of his essay.</p> <p>Two, TLC makes some decisions about how to resolve some unspecified TLA+ operations. For example, <code>CHOOSE x \in set: P(x)</code> is undefined if no value in <code>set</code> satisfies <code>P</code> (<a href="https://lamport.azurewebsites.net/tla/book-02-08-08.pdf" target="_blank"><em>Specifying Systems</em></a> pg 73). In TLC, it's an error: </p> <blockquote> <p>Attempted to compute the value of an expression of form<br/> CHOOSE x in S: P, but no element of S satisfied P.</p> </blockquote> <p>This catches a lot of design bugs where you expected something to exist but it doesn't. But it also makes specifying defaults awkward. What if I want to get the record with a certain id, but if there isn't one, return a default value <code>NoRecord</code>? Then I have to write this:</p> <div class="codehilite"><pre><span></span><code>RetrieveRecord(id) == IF \E r \in records: r.id = id THEN CHOOSE r \in records: r.id = id ELSE NoRecord </code></pre></div> <p>If the condition is longer, this gets more cumbersome, and there's always a risk of them getting out of sync. </p> <p>The upside of TLC being separate from TLA+ is that we can add features to TLC without changing the overall semantics of TLA+. That's what Markus is doing in the <a href="https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tla2sany/StandardModules/TLC.tla" target="_blank">TLC module</a>: things like <code>TLCGet</code> and <code>Assert</code> adds new features to <em>model checking TLA+</em> without adding new syntax to TLA+. It should be possible to make a new operator <code>Try(Op, Fail)</code> that evaluates <code>Op</code> and, if it fails, returns <code>Fail</code> instead. Then we can just do</p> <div class="codehilite"><pre><span></span><code>RetrieveRecord(id) == Try(CHOOSE r \in records: r.id = id, NoRecord) </code></pre></div> <p>With that in mind, most of the things I want in TLA+ are actually things I want in TLC, with one exception. </p> <h2>Things I wished were in TLA+/TLC</h2> <h3>Try(Op, Fail)</h3> <p>It'd also be nice for equality checking. Right now <code>"2" = 2</code> raises an error, even if I want it to be false.</p> <p>(Could nested <code>Try</code>s lead to ambiguous behavior? Something to think about.)</p> <h3>A more obvious set filtering</h3> <p>This is the only change here that would affect TLA+ itself and not just TLC. This is map and filter in TLA+:</p> <div class="codehilite"><pre><span></span><code>\* Map {M(x): x \in set} \* Filter {x \in set: F(x)} </code></pre></div> <p>Beginners can never remember which is which. Even experienced people often forget which is which! I'd like a different keyword for each, so we could instead write</p> <div class="codehilite"><pre><span></span><code>\* Filter {x \in set WHERE F(x)} \* Mapfilter {M(x): x \in set WHERE F(x)} </code></pre></div> <p>Actual keyword may vary. </p> <h3>A linter</h3> <p>In the past few years TLA+ tooling has gotten a <em>lot</em> better. Among other things, there's now a <a href="https://github.com/tlaplus/vscode-tlaplus" target="_blank">debugger</a>, a <a href="https://github.com/tlaplus-community/tree-sitter-tlaplus" target="_blank">treesitter plugin</a>, and an <a href="https://will62794.github.io/tla-web/#!/home?specpath=.%2Fspecs%2FTwoPhase.tla" target="_blank">interactive spec explorer</a>. I hope we soon see a linter, something that could detect TLA+ snippets with valid-but-unexpected behavior. In particular, something that could detect this:</p> <div class="codehilite"><pre><span></span><code>/\ A /\ B => C </code></pre></div> <p>That extra space before <code>=></code> means its parsed as <code>A /\ (B => C)</code>, not as the expected <code>(A /\ B) => C</code>. I once lost a day to this. I know at least three other people who also lost a day to this. It is 100% valid TLA+ and we desperately need a tool to tell us when it happens because <em>my gosh</em> is it a morale-killer. </p> <div class="subscribe-form"></div> <h3>Support for the forbidden operators</h3> <p>There are four operators that are unsupported by TLC and virtually unused in any specs: <code>\cdot</code>, <code>\EE</code>, <code>\AA</code>, and <code>-+-></code>. I have no idea how to use <code>\cdot</code> but Markus is steadily adding support for it. <code>\AA</code> exists for completeness and even Lamport can't think of a good use for it. <code>-+-></code> could be useful (It's the LTL <a href="https://en.wikipedia.org/wiki/Linear_temporal_logic#Weak_until_and_strong_release" target="_blank">weak release</a>), but nobody's paid it any attention.</p> <p><code>\EE</code> is the interesting one, in that we know <em>exactly</em> how useful it would be. <code>\EE x: P(x)</code> is the <strong>temporal quantifier</strong> and is defined as "there exists a sequence of <code>x</code>s that make the temporal formula <code>P(x)</code> true for every step of the behavior. Critically, <code>x</code> can be a <em>different</em> value in each step, whereas <code>\E x: P(x)</code> would require the <em>same</em> value in each step.</p> <p>Why is this so important? One word: <a href="https://hillelwayne.com/post/refinement/" target="_blank">refinement</a>.</p> <p>Say we have an abstract server model and we want make a more detailed version of just the <em>server</em>. We can <em>refine</em> it by instantiating the abstract spec like this:</p> <div class="codehilite"><pre><span></span><code>Abstract == INSTANCE AbstractServer WITH abstract_var1 <- formula1, abstract_var2 <- formula2, \* ... Refinement == Abstract!Spec </code></pre></div> <p>See the above link for more details. The trouble is that we need to apply substitutions for <em>every</em> variable. If the abstract spec models both the server and the client, our refinement also needs to refine the client state, too! And more frustratingly, if the abstract spec has some kind of bookkeeping variable, say a <a href="https://learntla.com/core/functions.html#state-sweeping" target="_blank">state-sweeper</a> or <a href="https://learntla.com/topics/aux-vars.html" target="_blank">auxiliary variable</a>, you have to refine that, too.</p> <p>But TLA+ provides an out here. Using <code>\EE</code>, we can write the refinement like this:</p> <div class="codehilite"><pre><span></span><code>Abstract(aux_var, client_var) == INSTANCE AbstractServer WITH abstract_var1 <- formula1, abstract_var2 <- formula2, \* ... Refinement == \EE a, c: Abstract(a, c)!Spec </code></pre></div> <p>Lamport calls this <strong>variable hiding</strong> (<em>SS</em> pg 41).</p> <p>Now I don't know much about the internals of TLC, but my best guess is that <code>\EE</code> would be <em>extraordinarily</em> expensive to check. I think a regular refinement you can check as you go, since you can generate the full abstract transition at the same time you generate the main spec's transition. But with variable hiding, you only have information to create <em>part</em> of the abstract transition. I think the only way to do it would be to generate the abstract state graph first and then look for a transition that matches your current transition.</p> <p>That is <em>at the very least</em> an NP-Complete problem, one you have to compute for <em>every edge in your refinement graph</em>. But maybe there are some special cases (like aux variables that do not affect behavior) which would be cheaper to check?</p> <h3>\E-stealing</h3> <p>This one's both the most niche and probably the hardest to implement, so I don't expect to see it anytime soon. But this is <em>my</em> newsletter and it bothers <em>me</em> specifically.</p> <p>In <a href="https://hillelwayne.com/post/composing-tla/" target="_blank">Composing TLA+ Specifications with State Machines</a> I used this trick to compose two specs:</p> <div class="codehilite"><pre><span></span><code>Action1 == /\ state = "start" /\ DoThing /\ state' = "wait" Sync == LET i == << state, state' >> IN CASE i == << "start", "wait" >> -> SomeSyncRules [] << "wait, start" >> -> DifferentSyncRules </code></pre></div> <p>So far, so good. But what if I take a nondeterministic action?</p> <div class="codehilite"><pre><span></span><code>Action2 == /\ state = "wait" /\ \E x \in set: DoThing(x) /\ state' = "start" Sync == LET i == << state, state' >> IN CASE i == << "start", "wait" >> -> SomeSyncRules [] << "wait, start" >> -> ??? </code></pre></div> <p>There's no way for the body of <code>Sync</code> to <em>know</em> which value <code>Action2</code> picked for <code>x</code>. I've found <code>Sync</code> actions are a great way to compose specifications, and this "opaqueness" limits how strong it can be. It'd be great to have some way of side-channeling out what <code>x</code> was picked, without changing the essential semantics of <code>Action2</code>. Preferably without even needing to modify it.</p> <p>This might also be useful for <a href="https://arxiv.org/abs/2006.00915" target="_blank">test case generation</a>, since you can augment the output traces with more information. </p> <p>I have no idea what this would look like or how it would behave. It's just a pipe dream of mine.</p> <h3>Other pipe dreams</h3> <p>A better function update syntax. Some way to <a href="https://www.hillelwayne.com/post/tla-adt/#dynamic-stacks" target="_blank">partially refine one spec into multiple</a>, seamlessly upgrading their individual variables into function variables (without <a href="https://www.hillelwayne.com/post/tla-adt/#fixing-the-action" target="_blank">too much boilerplate</a>). <del>Being able to inline a config file in a spec so you don't need two separate files.</del>[^inlining] Being able to call tlc without a config file and passing in flags. Support for <code>-+-></code>, just for giggles. Clock variables. Non-latex ASCII syntax. UNCHANGED (all vars except <code><<a, b>></code>). Parallelized liveness checking. A tree that grants all my wishes.</p> <h2>[^inlining]: This is now provisionally possible! You can see an example <a href="https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/test-model/Github866.tla" target="_blank">here</a>, run it with <code>tlc -config Github866.tla Github866.tla</code></h2> <h3>Comment Section</h3> <p>Buttondown now has comments! I'm experimentally enabling it for now, so you should be able to comment on the <a href="" target="_blank">email's archive page</a>. Again, <em>this is experimental</em>. If I end up having to spend all my time community moderating, comments are going down again. Be nice to each other.</p> <div class="footnote"> <hr/> <ol> <li id="fn:tuple-quantification"> <p><code>CHOOSE << x, y >> \in set \X set: x < y</code>. <a class="footnote-backref" href="#fnref:tuple-quantification" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 04 Sep 2024 17:00:19 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/</guid></item><item><title>State and time are the same thing</title><link>https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/</link><description><p style="height:16px; margin:0px !important;"></p> <h2>Time is state</h2> <p>Imagine I put an ordinary ticking <a href="https://en.wikipedia.org/wiki/Quartz_clock" target="_blank">quartz clock</a> in an empty room. I walk in, and ten minutes later I walk out with two photograph prints.<sup id="fnref:prints"><a class="footnote-ref" href="#fn:prints">1</a></sup> In the 1st one, the second hand is pointing at the top of the clock, in the 2nd it's pointing at the bottom. Are these two copies of the same photo, or are they two different pictures?</p> <p><img alt="A quartz clock with minute and second hands" class="newsletter-image" src="https://assets.buttondown.email/images/a6756666-c6f1-4ac7-9445-5ba6efa1406b.png?w=960&fit=max"/> </p> <p>There's no trick here, the answer is "different photos". Since the clock looks different, time must have passed between the two. More formally, we can represent the clock as a <strong>state vector</strong> of <code>(hours, minutes, seconds)</code>. Since the two pictures have different state vectors, they must represent different photos.</p> <p>Now I repeat the process and come out with two prints, both showing the same time. Are these two copies of the same photo or two different photos?</p> <p>It's <em>possible</em> for the pictures to be different but there's no way to be certain. I could have taken the two photos half a second apart so that time passed but the clock didn't tick yet. There's no observational difference between "time didn't pass" and "time passed but the state vector is the same". We can model time only passing in one second increments, as any shorter passage of time is not reflected in the state vector.</p> <p>Things would be different if you had access to the clock internals. The clock is powered by a quartz crystal that oscillates at approximately 2^15 hz, and a digital circuit inside the clock is counting that number as "one second". If you could read the memory inside the clock, then you could distinguish "00:00:15" and "00:00:15 + 2^14 oscillations".</p> <p>But in our current system that state is internal to the watch. Until the circuits turn the internal value into an observable value, we cannot recognize the passage of time for the clock.</p> <p>The only way we can see the passage of time is by measuring changes in observable state.</p> <div class="subscribe-form"></div> <h2>State is time</h2> <p>Pseudocode snippet:</p> <div class="codehilite"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>I'm trying to say that we pass a reference of a variable into <code>f</code>, so <code>int x = 1; f(x); print(x)</code> will output 3. Calling <code>f(x)</code> permanently splits our program into two eras. Before <code>f(x)</code>, all calculations with <code>x</code> will get one thing. <em>Anno Effexi</em>, all calculations with <code>x</code> will get another thing. The update to <code>x</code> advanced time. </p> <p>Now a more complicated case:</p> <div class="codehilite"><pre><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="o">++</span><span class="p">;</span> <span class="w"> </span><span class="o">*</span><span class="n">c</span><span class="o">++</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>Does this advance time one step or two? Depends on whether the program is single-threaded or concurrent. If it's single threaded, when <code>f</code> executes there's nothing else that can read x in between the two updates, so the first mutation is "internal". Externally it looks like there was only one observable mutation <code>x += 2</code>. On the other hand, if the program is concurrent, it's possible for some other thread or w/e to read x in between the two statements of <code>f</code>. That makes the internal mutation observable, so time is advanced twice. There are now three eras of our program with different possible behaviors.</p> <p>Changes in state matter in that they create new times.</p> <h3>The consequences</h3> <p>Some function programmers stay "shared mutable state is the enemy". I think it's more like "time is the enemy", and time represents itself as mutable state. If a state update is <em>purely internal</em> and <em>cannot</em> affect the observable state (such as a statement in an uncommitted transaction), then it does not advance time.</p> <p>I find this a good model to reason about abstract systems. It also motivates formal methods concepts like bisimulation and <a href="https://hillelwayne.com/post/refinement/" target="_blank">refinement</a>, which I really need to do an updated post on.</p> <div class="footnote"> <hr/> <ol> <li id="fn:prints"> <p>I've got a GendankenWorks instant digital camera that takes digital pictures but lets me print them too. I'm sure <em>someone</em> sells something like this. <a class="footnote-backref" href="#fnref:prints" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 27 Aug 2024 18:13:37 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/</guid></item><item><title>An idea for teaching formal methods better</title><link>https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/</link><description><p>I was recently commissioned by a company to make a bespoke <a href="https://www.learntla.com" target="_blank">TLA+</a> workshop with a strong emphasis on reading specifications. I normally emphasize <em>writing</em> specs, so this one will need a different approach.</p> <p>While working on it, I had an idea that <em>might</em> make teaching TLA+— and other <a href="https://buttondown.com/hillelwayne/archive/a-very-brief-intro-to-formal-methods-aka-my-job/" target="_blank">formal methods</a>— a little easier.</p> <h2>Pseudospecs</h2> <p>There are two problems to reading a spec:</p> <ol> <li>Learning the mental model of what TLA+ is actually <em>doing</em></li> <li>Learning how to actually read TLA+.</li> </ol> <p>The second problem is way, <em>way</em> bigger than it seems, because TLA+ has immense expressive power packed in a scary-looking syntax. Like take this spec:</p> <div class="codehilite"><pre><span></span><code>---- MODULE foo ---- CONSTANT Servers VARIABLE counters Init == counters = [s \in Servers |-> 0] Zero(s) == /\ counters[s] > 0 /\ counters' = [counters EXCEPT ![s] = 0] Inc(s) == counters' = [counters EXCEPT ![s] = @ + 1] Next == \/ \E s \in Servers: \/ Zero(s) \/ Inc(s) Spec == Init /\ [][Next]_counters ==== </code></pre></div> <p>What's <code>/\</code>? What's <code>![s]</code>? What's <code>[][Next]_counters</code>? To understand this spec, I have to <em>understand how to read TLA+</em>. This isn't an insurmountable obstacle, because otherwise <em>nobody</em> would know TLA+, but it could be the difference between "10 people learn TLA+" and "7 people learn TLA+". </p> <p>My idea is to provide the spec along with a pseudospec, which could look like this:</p> <div class="codehilite"><pre><span></span><code>Params {servers} Initialize with { counters: Map(s in Servers: 0) } action Zero(s) { requires { counters[s] > 0 } counters[s] := 0 } action Inc(s) { counters[s] +:= 1 } action Next { pick some s in Servers and { either do Zero(s) or do Inc(s) } </code></pre></div> <p>(Still working on what to do about the <code>Spec</code> operator.)</p> <p>Couple things to notice. One: it's not a consistent translation between two languages, but between language and meaning. There's no formal grammar for the pseudospec. I need inconsistency for reasons I'll talk about later. </p> <p>Two: the translation is not purely local. Things get moved around a little bit. <code>Zero</code> being an action affects how I translate <code>\E</code>.</p> <p>Three: the translation is kind of <a href="https://en.wikipedia.org/wiki/Metaphrase" target="_blank">metaphrasic</a>. It's close to, but not exactly, a line-by-line translation. A person who has both files open in a split can correlate the spec and pseudospec.<sup id="fnref:pluscal"><a class="footnote-ref" href="#fn:pluscal">1</a></sup></p> <p>Most directly, this will help people understand what the spec is doing, which gives me a basis to teach model-checking, safety properties, etc. But I think this will also makes learning the TLA+ syntax easier, by acting like an answer sheet. The student can read the TLA+ and, if they get stuck, look at the pseudospec for meaning. </p> <p>My moonshot hope is that this also helps with writing specs, by giving people a clearer sense of the standard TLA+ idioms. I can translate the same syntax slightly differently depending on the way it's used, and whether it's a general construct or one for just the spec. That might be doing too much with this concept, though. </p> <h2>Problems I see</h2> <div class="subscribe-form"></div> <p>First is that there's lots of nuances that would be lost in the translation. In writing</p> <div class="codehilite"><pre><span></span><code>Init == /\ x \in 1..5 --> Initialize with { x: pick some in 1..5 } </code></pre></div> <p>I lose the idea that <code>Init</code> <em>isn't special</em>, it's just a boolean operator like anything else. The student will have to unlearn that misconception if they ever need to do tricks like <a href="https://learntla.com/topics/optimization.html#ignore-part-of-the-state-space" target="_blank">FastInit</a>. </p> <p>This is an essential tradeoff in pedagogy: is it better to teach the full picture now or the easy thing first and fix the misconception later? The teacher side of me knows that the white lie is better in the long run, but the expert side of me <em>hates</em> this. </p> <p>Second, not every TLA+ symbol has a 1-1 mapping with English. I did the translation</p> <div class="codehilite"><pre><span></span><code>\E s \in Servers: \/ Zero(s) \/ Inc(s) --> pick some s in Servers and { either do Zero(s) or do Inc(s) } </code></pre></div> <p>That's because <code>Zero</code> and <code>Inc</code> are actions— things that update state. If they were regular operators, If P and Q are regular operators, I'd translate it like this:</p> <div class="codehilite"><pre><span></span><code>exists an s in S { P(s) || Q(s) } </code></pre></div> <p>Depending on the context, I'll translate <code>\E</code> in two different ways.</p> <p>This ties back to problem #1. In TLA+, <em>these are the same thing</em>. Performing an action is <em>literally</em> saying that it's a true description of the next-state relation. This has the consequence of <em>looking</em> like it updates the state relation, but that's only in certain (exceptionally common) circumstances. This is all incredibly elegant and intuitive to a TLA+ expert, but not for a beginner. So I have to introduce some inconsistencies. </p> <p>Problem #3 is just how much syntax I'll need to translate. What do I do for <code>[A -> B]</code>, <code>WF_vars</code>, or <code>\AA</code>? I can try to avoid them for the early specs but can't do that forever. </p> <p>Overall, though, I think the potential benefit of easier learning will outweigh the drawbacks.</p> <h3>Will this work?</h3> <p>We'll find out!</p> <div class="footnote"> <hr/> <ol> <li id="fn:pluscal"> <p>Experienced TLA+ users might notice that this is inspired by both PlusCal and <a href="https://github.com/informalsystems/quint" target="_blank">Quint</a>. I can't just teach those because the goal is to get people use TLA+ proper. (Also we really need a word for "experienced TLA+ user", like "Pythonista" or "Rustacean") <a class="footnote-backref" href="#fnref:pluscal" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 21 Aug 2024 16:05:27 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/</guid></item><item><title>Texttools dot py</title><link>https://buttondown.com/hillelwayne/archive/texttools-dot-py/</link><description><p>I make a lot of personal software tools. One of these is "texttools.py", which is easiest to explain with an image:</p> <p><img alt="A GUI with the first three lines of this newsletter in the top, and the bottom has the three lines with markdown quote prefixes" class="newsletter-image" src="https://assets.buttondown.email/images/ddf06d0f-d8b9-4ae4-8c10-b275e104731e.png?w=960&amp;fit=max"/> </p> <p>Paste text in the top box, choose a transform, output appears in the bottom box. I can already do most of these transformations in vim, or with one of the many online tools out there, but I prefer my script for two reasons:</p> <ol> <li>There's no context switching. I don't have to leave my current tab or vim buffer and worry about cleanup later. One hotkey opens the GUI, <code>&lt;esc&gt;</code> closes it, with no break in my workflow.</li> <li>It loads in &lt;10 ms.</li> </ol> <p>It took me like an hour to make and I use it all the time. And it's small enough that I just share the whole script here.</p> <h2>How it works</h2> <p>Texttools is a python script running a <a href="https://docs.python.org/3/library/tkinter.html" target="_blank">tkinter</a> GUI. I used tkinter because it's a builtin; I would generally <em>not</em> recommend it if you have any better options. On the plus side, being a builtin means you don't need to install a package to use this yourself.</p> <div class="subscribe-form"></div> <div class="codehilite"><pre><span></span><code><span class="kn">import</span> <span class="nn">tkinter</span> <span class="k">as</span> <span class="nn">tk</span> <span class="kn">from</span> <span class="nn">tkinter</span> <span class="kn">import</span> <span class="n">N</span><span class="p">,</span> <span class="n">S</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">ttk</span> <span class="c1"># Complex transforms go here,</span> <span class="c1"># Simple transforms are just lambdas</span> <span class="k">def</span> <span class="nf">_wordcount</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="s2">"Returns a tuple of linecount, wordcount, charcount"</span> <span class="k">return</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">splitlines</span><span class="p">()),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">))</span> <span class="n">transforms</span> <span class="o">=</span> <span class="p">[</span> <span class="c1"># Transforms go here, for example</span> <span class="p">{</span><span class="s2">"name"</span><span class="p">:</span> <span class="s2">"One line"</span><span class="p">,</span> <span class="s2">"transform"</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">x</span><span class="o">.</span><span class="n">splitlines</span><span class="p">())}</span> <span class="p">,{</span><span class="s2">"name"</span><span class="p">:</span> <span class="s2">"Line/Word/Char"</span><span class="p">,</span> <span class="s2">"transform"</span><span class="p">:</span> <span class="n">_wordcount</span><span class="p">}</span> <span class="p">]</span> <span class="k">class</span> <span class="nc">GUI</span><span class="p">():</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Tk</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="c1"># start with no transform</span> <span class="bp">self</span><span class="o">.</span><span class="n">layout_gui</span><span class="p">()</span> <span class="k">def</span> <span class="nf">layout_gui</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span> <span class="o">=</span> <span class="n">ttk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s2">"3 3 12 12"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">"Text Tools"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">S</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">columnconfigure</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">rowconfigure</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Text</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">undo</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Text</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">undo</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Listbox</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mainframe</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">rowspan</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">transforms</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">,</span> <span class="n">t</span><span class="p">[</span><span class="s2">"name"</span><span class="p">])</span> <span class="c1"># Keyboard bindings</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Escape&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">quit</span><span class="p">())</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Tab&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">focus</span><span class="p">())</span> <span class="c1"># vvv makes clicking or pressing enter change the transform</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Button-1&gt;"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">select_transform</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Return&gt;"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">select_transform</span><span class="p">)</span> <span class="c1"># vvv makes anything typed in update the output</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s2">"&lt;Key&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">_</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">after</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">focus</span><span class="p">()</span> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">content_box</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">)</span> <span class="n">content</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span><span class="p">(</span><span class="n">content</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">tk</span><span class="o">.</span><span class="n">END</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_box</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="s1">'1.0'</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> <span class="k">def</span> <span class="nf">select_transform</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">selection</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transform_box</span><span class="o">.</span><span class="n">curselection</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">active_transform</span> <span class="o">=</span> <span class="n">transforms</span><span class="p">[</span><span class="n">selection</span><span class="p">][</span><span class="s2">"transform"</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">()</span> <span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span> <span class="k">pass</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="c1"># Entry point for pyproject.toml</span> <span class="n">gui</span> <span class="o">=</span> <span class="n">GUI</span><span class="p">()</span> <span class="n">gui</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">mainloop</span><span class="p">()</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span> <span class="n">main</span><span class="p">()</span> </code></pre></div> <p>Man I forget how much I dislike tkinter until I have to look at it again. If you want to add your own text tools, just put a new item in the global <code>transforms</code> array.</p> <p>To make it easier to run the script, I put it in a "toolkit" repo with this <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/" target="_blank">pyproject.toml</a>:</p> <div class="codehilite"><pre><span></span><code><span class="k">[project]</span> <span class="n">name</span><span class="o">=</span><span class="s2">"toolkit"</span> <span class="n">version</span><span class="o">=</span><span class="s2">"0.0.1"</span> <span class="n">requires-python</span><span class="o">=</span><span class="s2">"&gt;=3.8"</span> <span class="k">[project.gui-scripts]</span> <span class="n">toolkit-texttools</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"toolkit.texttools:main"</span> </code></pre></div> <p>Then running <code>pip install -e .</code> creates a <code>toolkit-texttools</code> binary (or in my case a <code>.exe</code>).</p> <p>Finally, I wrote an <a href="https://www.hillelwayne.com/post/ahk-scripts-project/" target="_blank">AutoHotKey script</a> so I could load it with a keyboard shortcut:</p> <div class="codehilite"><pre><span></span><code><span class="c1">; &amp; numpagedown + t both pressed at same time</span> <span class="n">NumpadPgDn</span> <span class="o">&amp;</span> <span class="n">t</span><span class="o">::</span> <span class="n">toggle_app</span><span class="p">(</span><span class="s">"Text Tools"</span><span class="p">,</span> <span class="s">"toolkit-texttools.exe"</span><span class="p">)</span> </code></pre></div> <p>I like mapping stuff to the numpad because it's guaranteed to not interfere with any OS or program-specific hotkeys.</p> <p>Short newsletter this week because I'm still recovering from jetlag. See you all next week!</p></description><pubDate>Wed, 14 Aug 2024 17:19:46 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/texttools-dot-py/</guid></item><item><title>Why I prefer rST to markdown</title><link>https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/</link><description><p>I just published a new version of <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>! v0.2 has epub support, content on constraint solving and formal specification, and more! Get it <a href="https://leanpub.com/logic/" target="_blank">here</a>.</p> <p>This is my second book written with <a href="https://www.sphinx-doc.org/en/master/" target="_blank">Sphinx</a>, after the new <a href="https://www.learntla.com/" target="_blank">Learn TLA+</a>. Sphinx uses a peculiar markup called <a href="https://docutils.sourceforge.io/rst.html" target="_blank">reStructured Text</a> (rST), which has a steeper learning curve than markdown. I only switched to it <em>after</em> writing a couple of books in markdown and deciding I needed something better. So I want to talk about why rst was that something.<sup id="fnref:rst"><a class="footnote-ref" href="#fn:rst">1</a></sup></p> <h2>Why rst is better</h2> <p>The most important difference between rst and markdown is that markdown is a lightweight representation of html, while rst is a midweight representation of an abstract documentation tree.</p> <p>It's easiest to see this with a comparison. Here's how to make an image in markdown:</p> <div class="codehilite"><pre><span></span><code> </code></pre></div> <p>Technically, you don't even need a parser for this. You just need a regex to transform it into <code>&lt;img alt="alttext" src="example.jpg"/&gt;</code>. Most modern markdown engines <em>do</em> parse this into an intermediate representation, but the <em>essence</em> of markdown is that it's a lightweight html notation.</p> <p>Now here's how to make an image in rst:</p> <div class="codehilite"><pre><span></span><code><span class="p">..</span> <span class="ow">image</span><span class="p">::</span> example.jpg <span class="nc">:alt:</span> alttext </code></pre></div> <p><code>.. image::</code> defines the image "directive". When Sphinx reads it, it looks up the registered handler for the directive, finds <code>ImageDirective</code>, invokes <code>ImageDirective.run</code>, which returns an <code>image_node</code>, which is an object with an <code>alt</code> field containing "alttext". Once Sphinx's processed all nodes, it passes the whole doctree to the HTML Writer, which looks up the rendering function for <code>image_node</code>, which tells it to output an <code>&lt;image&gt;</code> tag.</p> <p>Whew that's a mouthful. And for all that implementation complexity, we get… an interface that has 3x the boilerplate as markdown.</p> <p>On the other hand, the markdown image is hardcoded as a special case in the parser, while the rst image is not. It was added in the exact same way as every other directive in rst: register a handler for the directive, have the handler output a specific kind of node, and then register a renderer for that node for each builder you want.</p> <p>This means you can extend Sphinx with new text objects! Say you that instead of an <code>&lt;image&gt;</code>, you want a <code>&lt;figure&gt;</code> with a <code>&lt;figcaption&gt;</code>. In basic markdown you have to manually insert the html, with Sphinx you can just register a new <code>figure</code> directive. You can even make your <code>FigureDirective</code> subclass <code>ImageDirective</code> and have it do most of the heavy lifting.</p> <p>The second benefit is more subtle: you can transform the doctree before rendering it. This is how Sphinx handles cross-referencing: if I put a <code>foo</code> anchor in one document and <code>:ref:`image &lt;foo&gt;`</code> in another, Sphinx will insert the right URL during postprocessing. The transformation code is also first-class with the rest of the build process: I can configure a transform to only apply when I'm outputting html, have it trigger in a certain stage of building, or even remove a builtin transform I don't want to run.</p> <p>Now, most people may not need this kind of power! Markdown is ubiquitous because it's lightweight and portable, and rst is anything but. But <em>I</em> need that power.</p> <div class="subscribe-form"></div> <h3>One use case</h3> <p><em>Logic for Programmers</em> is a math-adjacent book, and all good math books need exercises for the reader. It's easier to write an exercise if I can put it and the solution right next to each other in the document. But for readers, I want the solutions to show up in the back of the book. I also want to link the two together, and since I might want to eventually print the book, the pdfs should also include page references. Plus they need to be rendered in different ways for latex (pdf) output and epub output. Overall lots of moving parts.</p> <p>To handle this I wrote my own exercise extension.</p> <div class="codehilite"><pre><span></span><code><span class="c">.. in chapter.rst</span> <span class="p">..</span> <span class="ow">exercise</span><span class="p">::</span> Fizzbuzz <span class="nc">:name:</span> ex-fizzbuzz An exercise <span class="p">..</span> <span class="ow">solution</span><span class="p">::</span> ex-fizzbuzz A solution <span class="c">.. in answers.rst</span> <span class="p">..</span> <span class="ow">solutionlist</span><span class="p">::</span> </code></pre></div> <p>How these nodes are processed depends on my compilation target. I like to debug in HTML, so for HTML it just renders the exercise and solution inline.</p> <p>When generating epub and latex, though, things works a little differently. After generating the whole doctree, I run a transform that moves every solution node from its original location to under <code>solutionlist</code>. Then it attaches a reference node to every exercise, linking it to the <em>new</em> solution location, and vice versa. So it starts like this (using Sphinx's "pseudoxml" format): </p> <div class="codehilite"><pre><span></span><code>--<span class="w"> </span>chapter.rst <span class="nt">&lt;exercise_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;title&gt;</span> <span class="w"> </span>Fizzbuzz <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>An<span class="w"> </span>exercise <span class="nt">&lt;solution_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>A<span class="w"> </span>solution --<span class="w"> </span>answers.rst <span class="nt">&lt;solutionlist_node&gt;</span> </code></pre></div> <p>And it becomes this:</p> <div class="codehilite"><pre><span></span><code>--<span class="w"> </span>chapter.rst <span class="nt">&lt;exercise_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;title&gt;</span> <span class="w"> </span>Fizzbuzz <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>An<span class="w"> </span>exercise <span class="w"> </span><span class="nt">&lt;exsol_ref_node</span><span class="w"> </span><span class="na">refuri=</span><span class="s">"/path/to/answers#ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span>Solution --<span class="w"> </span>answers.rst <span class="nt">&lt;solutionlist_node&gt;</span> <span class="w"> </span><span class="nt">&lt;solution_node</span><span class="w"> </span><span class="na">ids=</span><span class="s">"ex-fizzbuzz-sol"</span><span class="nt">&gt;</span> <span class="w"> </span><span class="nt">&lt;paragraph&gt;</span> <span class="w"> </span>A<span class="w"> </span>solution <span class="w"> </span><span class="nt">&lt;exsol_ref_node</span><span class="w"> </span><span class="na">refuri=</span><span class="s">"/path/to/chapter#ex-fizzbuzz"</span><span class="nt">&gt;</span> <span class="w"> </span>(back) </code></pre></div> <p style="height:16px; margin:0px !important;"></p> <p>The Latex builder renders this by wrapping each exercise and solution in an <a href="https://ctan.org/pkg/exercise" target="_blank">answers environment</a>, while the epub builder renders the solution as a <a href="https://help.apple.com/itc/booksassetguide/en.lproj/itccf8ecf5c8.html" target="_blank">popup footnote</a>.<sup id="fnref:exsol_ref"><a class="footnote-ref" href="#fn:exsol_ref">2</a></sup> Making this work:</p> <p><img alt="An example of solution popups on an epub reader" class="newsletter-image" src="https://assets.buttondown.email/images/8a7d66e3-56bd-4b7a-95ac-d4fdf88047c7.png?w=960&amp;fit=max"/> </p> <p>It's a complex dance of operations, but it works enormously well. It even helps with creating a "free sample" subset of the book: the back of the free sample only includes the solutions from the included subset, not the whole book!</p> <h3>"But I hate the syntax"</h3> <p>When I gush about rST to other programmers, this is the objection I hear the most: it's ugly. </p> <p>To which I say, are you really going to avoid using a good tool just because it makes you puke? Because looking at it makes your stomach churn? Because it offends every fiber of your being?</p> <p>...Okay yeah that's actually a pretty good reason not to use it. I can't get into lisps for the same reason. I'm not going to begrudge anybody who avoids a tool because it's ugly.</p> <p>Maybe you'd find <a href="https://github.com/asciidoctor/asciidoctor" target="_blank">asciidoc</a> more aesthetically pleasing? Or <a href="https://mystmd.org/spec" target="_blank">MyST</a>? Or <a href="https://github.com/typst/typst" target="_blank">Typst</a>? Or <a href="https://docs.racket-lang.org/pollen/" target="_blank">Pollen</a>? Or even <a href="https://pandoc.org/MANUAL.html#extension-attributes" target="_blank">pandoc-extended markdown</a>? There are lots of solid document builders out there! My point isn't that sphinx/rst is exceptionally <em>good</em> for largescale documentation, it's that simple markdown is exceptionally <em>bad</em>. It doesn't have a uniform extension syntax or native support for pre-render transforms.</p> <p>This is why a lot of markdown-based documentation generators kind of hack on their own preprocessing step to support new use-cases, which works for the most part (unless you're trying to do something really crazy). But they have to work around the markdown, not in it, which limits how powerful they can be. It also means that most programmer tooling can't understand it well. There's LSP and treesitter for markdown and rst but not for gitbook-markdown or md-markdown or leanpub-markdown.<sup id="fnref:treesitter"><a class="footnote-ref" href="#fn:treesitter">3</a></sup></p> <p>But if you find a builder that uses markdown and satisfies your needs, more power to you! I just want to expose people to the idea that doc builders can be a lot more powerful than they might otherwise expect.</p> <hr/> <h3>No newsletter next week</h3> <p>I'll be in Hong Kong.</p> <h2>Update 2024-07-31</h2> <p>Okay since this is blowing up online I'm going to throw in a quick explanation of <em>Logic for Programmers</em> for all of the non-regulars here. I'm working on a book about how formal logic is useful in day-to-day software engineering. It starts with a basic rundown of the math and then goes into eight different applications, such as property testing, database constraints, and decision tables. It's still in the alpha stages but already 20k words and has a lot of useful content. You can find it <a href="https://leanpub.com/logic" target="_blank">here</a>. Reader feedback highly appreciated!</p> <div class="footnote"> <hr/> <ol> <li id="fn:rst"> <p>rst is actually independent of Sphinx, but everybody I know who writes rst writes it <em>because</em> they're using Sphinx, so I'll use the two interchangeably. Also typing rST is annoying so I'm typing rst. <a class="footnote-backref" href="#fnref:rst" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:exsol_ref"> <p>This is why I attach <code>exsol_ref_nodes</code> and not default <code>reference_nodes</code>. Sphinx's epub translator uses an attribute passlist I need to workaround in post-rendering. <a class="footnote-backref" href="#fnref:exsol_ref" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:treesitter"> <p>This is also one place where rst's ugly syntax works in its favor. I've got a treesitter query that changes the body of todo directives and <em>only</em> todo directives, which is only possible because the rst syntax tree is much richer than the markdown syntax tree. <a class="footnote-backref" href="#fnref:treesitter" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Wed, 31 Jul 2024 15:34:39 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/</guid></item><item><title>My patented Miracle Tonic would have prevented the CrowdStrike meltdown</title><link>https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/</link><description><p>Last Friday CrowdStrike did something really bad and it destroyed every airport in the world. I didn't bother to learn anything else about it because I was too busy writing my 10k whitepaper about how all the problems were all caused by one simple mistake: not drinking my patented Miracle Tonic™®.</p> <p>Developers who drink my Miracle Tonic write code 100% faster with 100% fewer bugs. This would have prevented the CrowdStrike outage, the 2016 DNC hack, Ariane 5, Therac-25, and that one moth caught in the Harvard Mark II. Developers are also happier at work, suffer no burnout, and keep all standups to <em>exactly</em> five minutes.</p> <p>The Miracle Tonic is so effective that it should be <em>immoral</em> not to drink it. It's like if surgeons didn't wash their hands. If you write code for a living and you don't drink my Miracle Tonic, do us all a favor and never touch a computer again. You idiot. You absolute moron. I can't believe you call yourself a "professional".</p> <p><strong>Frequently Asked Questions:</strong></p> <blockquote> <p>Do you have any actual evidence that Miracle Tonic actually helps programmers?</p> </blockquote> <p>Yes I do! All sorts of studies prove the effectiveness of Miracle Tonic:</p> <ol> <li>One survey found that 68% of devs drinking miracle tonic say their code is "awesome". That means it works!</li> <li>A <em>double-blind clinical trial</em> found that 7 undergraduates who were given Miracle Tonic wrote 10% better code (using the Hills-Bourne-Davidio Corrected Dolorimetry metric) than the control group (6 undergraduates who were punched in the face).</li> <li>Someone found twelve projects on GitHub that didn't use Miracle Tonic and they had 268% worse failure rates than this one project I did with it. That's a P value of 0.00004!</li> </ol> <p>That's so many studies! I can say with 100% confidence that Miracle Tonic is proven to work beyond a shadow of a doubt.</p> <blockquote> <p>I read a study saying that Miracle Tonic gives people headaches.</p> </blockquote> <p>Why are you trusting <em>studies</em>? Are you really gonna listen to some graduate student dweeb who's never been a <em>real programmer</em>?! If you're curious about Miracle Tonic, just try it and see for yourself.</p> <blockquote> <p>Are there any downsides to drinking Miracle Tonic?</p> </blockquote> <p>Of course, there is no silver bullet, everything is tradeoffs, etc etc etc. The downside of Miracle Tonic is it doesn't work if your company is a toxic feature factory that cares more about micromanaging mindless worker drones than cultivating good engineers. And don't drink it if you're allergic to peanuts.</p> <blockquote> <p>This tastes revolting.</p> </blockquote> <p>I've trained five Miracle Tonic Brand Ambassadors and they all tell me it's delicious. Your taste buds must be wrong.</p> <blockquote> <p>I tried drinking your Miracle Tonic and it didn't make me a better programmer.</p> </blockquote> <p><em>How dare you</em>. How dare you spread FUD about the most important programming technology ever made. Were you drinking exactly 12.063 mL at 67° C every 75 minutes? Yeah, thought not. Of course it's not going to work if you didn't follow it properly. And you'd know this if you took my $1500 three-day "how do drink Miracle Tonic" course. Which you didn't. <em>Get out of my sight.</em></p> <blockquote> <p>How does Miracle Tonic compare to competing products, like toad oil, Pirelli's Miracle Elixir, or Design-by-Contract?</p> </blockquote> <p>Fucking charlatans, all of them.</p> <hr/> <p>This is the part of my job I dread.</p> <p>I do formal methods for a living. Last year 100% of my income came from either writing specifications or training others how to write specifications. This year, due to a lot of work in diversifying my revenue streams, it will only be 90% of my income. This creates an immense pressure to become an ambulance chaser, to see ten billion dollars in economic damage as a chance to say "look at me!", a longshot chance to find the next source who'll pay my next month's rent.</p> <p>I'm also a True Believer. Of course formal methods would have prevented the CrowdStrike outage! It also would have prevented COVID-19, the Cuban Missile Crisis and the Lincoln assassination. Years of being an advocate have shaped my worldview to <em>always</em> see how it could have helped, how everything is just the right shape nail for the hammer I'm selling you.</p> <p>None of this depends on anything particular to formal methods, which is why advocates of every stripe are telling us "what they should have done", because every advocate is a true believer, and every advocate wants to get paid. I understand this all, and I get why people do this, but I hate feeling this way, that all this misery begets opportunity. I hate the pressure to say "they needed X" as fast as possible, before people lose interest, regardless of whether X would help or even if they were <em>already using</em> X. I hate the feeling that capitalizing on this might compromise my principles.</p> <p>Most of all though, I fear the slippery slope from advocating to grifting. There are people out there who saw the outage and got <em>excited</em>. Please, God, let me never be that.</p></description><pubDate>Tue, 23 Jul 2024 15:22:21 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/</guid></item><item><title>Keep perfecting your config</title><link>https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/</link><description><p>First of all, I wanted to extend a huge and heartfelt thank you to all of the people who bought <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a>. Seeing the interest is incredible motivation to continue improving it. If you read it and have feedback, please share it with me!</p> <p>Second, I have a new blogpost out: <a href="https://www.hillelwayne.com/post/toolbox-languages/" target="_blank">Toolbox Languages</a>. It's about five obscure languages I use that make certain hard problems I have much easier. I hope it encourages other people to talk about their toolbox languages (or share new ones with me!). Patreon blog notes (and the cut sixth language) <a href="https://www.patreon.com/posts/108180681" target="_blank">here</a>.</p> <p>Third, actual newsletter. </p> <h1>Keep perfecting your config</h1> <p>Last week I read the article <a href="https://arkadiuszchmura.com/posts/stop-perfecting-your-config/" target="_blank">stop perfecting your config</a>, which argues that people spend too much time tinkering with "editors, IDEs, terminals [and] other applications". I firmly believe the opposite, that people should spend <em>more</em> time tinkering. To be fair, the OP says</p> <blockquote> <p>Before I continue, let’s make one thing clear. By no means am I saying that there is something wrong with trying to master your tools or attempting to adjust them to suit your specific needs. It’s actually the opposite.</p> </blockquote> <p>So I read this more as "don't make my mistakes" than "don't do what I don't like."<sup id="fnref:caveat"><a class="footnote-ref" href="#fn:caveat">1</a></sup> At the same time, it's notable are "his mistakes"— what he counted as perfecting his configs. Reading the article, he mentions Neovim keymaps once, plugins once, and color schemes four times. </p> <p>There's an idea in programming language design called <a href="https://wiki.haskell.org/Wadler's_Law" target="_blank">Wadler's Law</a>, which is that the majority of discussion is on syntax and not semantics. I imagine this applies to configuration too: the majority of time spent "tinkering" is on layout and coloring and not on changes to the tool's behavior. <strong>This is a trap.</strong> You get the most value of "configuration" when you use it to improve your workflow.</p> <p>Here's a Neovim script I wrote a while back:</p> <div class="codehilite"><pre><span></span><code><span class="kr">function</span> <span class="nf">LoadLocal</span><span class="p">(</span><span class="n">local_task</span><span class="p">)</span> <span class="n">vim</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">local_task</span> <span class="o">=</span> <span class="n">local_task</span> <span class="kr">end</span> <span class="kr">function</span> <span class="nf">RunLocal</span><span class="p">()</span> <span class="n">vim</span><span class="p">.</span><span class="n">cmd</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">b</span><span class="p">.</span><span class="n">local_task</span><span class="p">)</span> <span class="kr">end</span> <span class="n">vim</span><span class="p">.</span><span class="n">cmd</span> <span class="s">[[command! -nargs=1 LoadLocal call v:lua.LoadLocal(&lt;f-args&gt;)]]</span> <span class="n">vim</span><span class="p">.</span><span class="n">keymap</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'n'</span><span class="p">,</span> <span class="s1">'gxl'</span><span class="p">,</span> <span class="n">RunLocal</span><span class="p">,</span> <span class="p">{</span><span class="n">desc</span><span class="o">=</span><span class="s2">"Buffer Task"</span><span class="p">})</span> </code></pre></div> <p>On calling <code>:LoadLocal foo</code> it loads <code>foo</code> as the local command for that specific buffer. Then, typing <code>gxl</code> executes <code>foo</code>. The command can be <em>anything</em>, up to and including shelling out to <code>!rm *</code>.</p> <p>Oh, also any other script can set <code>vim.b.local_task</code>.<sup id="fnref:exploit"><a class="footnote-ref" href="#fn:exploit">2</a></sup> <code>LoadLocal</code> can be used by the rest of my configs. When I open an XML file it sets the local task to the appropriate transformation script.</p> <p>Those eight lines have probably saved me at least eight hours of drudgework over their lifespan. <a href="https://www.hillelwayne.com/post/task-runner-neovim/" target="_blank">The full taskrunner has saved even more</a>. <em>That's</em> the kind of customization that's most useful.</p> <p>Anything that goes for an IDE goes double for the shell. I use a ton of shell scripts. </p> <h3>Customization teaches you</h3> <div class="subscribe-form"></div> <p>A common argument against customization is that you shouldn't do it until you know the tooling basics well. For example, you shouldn't install a filetree plugin for neovim because it comes with Netrw. In other words, there's a difference between the skill of using your tools and the skill of customizing them, and the first is more important than the second.</p> <p>But just as often customization promotes mastery, by smoothing out the starting friction with using a feature. Say you're trying to get more comfortable with vim macros. Here's a keymapping that could help:</p> <div class="codehilite"><pre><span></span><code><span class="n">vim</span><span class="p">.</span><span class="n">keymap</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'n'</span><span class="p">,</span> <span class="s2">"&lt;leader&gt;Q"</span><span class="p">,</span> <span class="s">[[:let @q = input("Edit macro:", @q)&lt;CR&gt;]]</span><span class="p">)</span> </code></pre></div> <p>Vim macros are stored in a text registry: recording the <code>@q</code> macro will overwrite whatever you copied to the <code>"q</code> register. This means that you can treat a macro <em>as</em> text. <code>input</code> will fill a prompt with the <code>@q</code> macro text, let you edit it as text, and then save your changes back to the register.</p> <p>This makes it so much easier to experiment with macros! If you screw up a macro while recording, you don't need to start over, you can just finish and make an edit afterwards. If you try a finished macro and it does something wrong, same thing, just undo and edit it.</p> <h3>Reasons not to customize</h3> <p>I've heard two other reasons why <em>not</em> to heavily customize your system:</p> <ol> <li>It'll be harder for other people to pair with you on your machine</li> <li>It'll be harder for you to work on machines without the customizations: servers you SSHed into, VMs, other people's computers, etc.</li> </ol> <p>(1) is only a problem if you're modifying default keybindings or adding surprising new ones. I deal with it by having two editors: a heavily customized Neovim for solo work, a vanilla VSCode (plus plugins) for collaboration.</p> <p>(2) is a bigger problem. I find the problem is how it messes with muscle memory. In vim I've mapped <code>H/L</code> to beginning/end of line when normally they mean "first/last line in view", and it always screws me up. It is so immensely frustrating. Other than that, though, I <em>generally</em> can tolerate losing my config for a bit. Most of the stuff that's most useful to me are things that aren't useful when I'm remoting, so I don't miss them. </p> <p>One good reason not to customize that I <em>haven't</em> heard: There is one more reason I've heard not to customize: you can easily break stuff that you don't know how to fix. This is more true with stuff like vim and shell that allow arbitrary scripts as config, less so with VSCode or the new crop of modal editors.</p> <div class="footnote"> <hr/> <ol> <li id="fn:caveat"> <p>Also <a href="https://arkadiuszchmura.com/posts/optimizing-your-workflow-does-matter-in-the-long-run/" target="_blank">he wrote positively about configuration later</a>. <a class="footnote-backref" href="#fnref:caveat" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:exploit"> <p>I swear to God if this leads to someone targeting an exploit at me I will quit programming forever <a class="footnote-backref" href="#fnref:exploit" title="Jump back to footnote 2 in the text">↩</a></p> </li> </ol> </div></description><pubDate>Tue, 16 Jul 2024 19:18:22 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/</guid></item><item><title>Logic for Programmers now in early access!</title><link>https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/</link><description><p>I am delighted to announce that <a href="https://leanpub.com/logic/" target="_blank">Logic for Programmers</a> is now available for purchase! While still in early access, it's almost 20,000 words, has 30 exercises, and covers a wide variety of logic applications: </p> <ul> <li>Property testing</li> <li>Functional correctness and contracts</li> <li>Formal verification</li> <li>Decision tables</li> <li>Database constraints</li> <li>Data modeling and alloy</li> <li>Constraint solving</li> <li>Logic programming</li> </ul> <p>I want to emphasize the book is early access; meaning it's not close to done yet. I'm releasing it now so I can get feedback from readers and use that to decide what I write next. But I'm only willing to release it because I think, even in its current state, it's worth reading. I'm just promising it'll be even better in a few months. </p> <h2>What's next</h2> <p>I think exercise solutions are broken on epub, so step one is getting that fixed. </p> <p>After that, I'm taking a short break. I put basically all of June into working on this and need to get back to blogging and client acquisition. Plus I need a bit of time to gather reader feedback. </p> <p>I'm thinking of new major releases being either every two weeks or every month or so. I'll upload new versions of the book between releases and then send a Leanpub publication announcement when we hit the next milestone.</p> <h3>PS</h3> <p>There's a free book coupon on the <a href="https://www.patreon.com/hillelwayne" target="_blank">Patreon</a>. Enough people have joined that I feel obligated to start posting more original content there <em>why does this keep happening to me</em></p></description><pubDate>Mon, 08 Jul 2024 17:33:18 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/</guid></item><item><title>Solving a math problem with planner programming</title><link>https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/</link><description><p>The deadline for the <a href="https://buttondown.email/hillelwayne/archive/logic-for-programmers-update/" target="_blank">logic book</a> is coming up! I'm hoping to have it ready for early access by either the end of this week or early next week. During a break on Monday I saw this interesting problem on <a href="https://math.stackexchange.com/questions/4939319/how-many-steps-are-needed-to-turn-one-a-into-at-least-100-000-as-using-only" target="_blank">Math Stack Exchange</a>:</p> <blockquote> <p>Suppose that at the beginning there is a blank document, and a letter "a" is written in it. In the following steps, only the three functions of "select all", "copy" and "paste" can be used. </p> <p>Find the minimum number of steps to reach at least 100,000 a's (each of the three operations of "select all", "copy" and "paste" is counted as one step). If the target number is not specified, and I want to get the exact amount of a, is there a general formula?</p> </blockquote> <p>The first two answers look for analytic solutions. The <a href="https://math.stackexchange.com/a/4939673" target="_blank">last answer</a> shares a C++ program that finds it via breadth-first search. I'll reproduce it here:</p> <div class="codehilite"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;iostream&gt;</span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;queue&gt;</span> <span class="k">enum</span><span class="w"> </span><span class="nc">Mode</span> <span class="p">{</span> <span class="w"> </span><span class="n">SELECT</span><span class="p">,</span> <span class="w"> </span><span class="n">COPY</span><span class="p">,</span> <span class="w"> </span><span class="n">PASTE</span> <span class="p">};</span> <span class="k">struct</span><span class="w"> </span><span class="nc">Node</span> <span class="p">{</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">noOfAs</span><span class="p">;</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">steps</span><span class="p">;</span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">noOfAsCopied</span><span class="p">;</span> <span class="w"> </span><span class="n">Mode</span><span class="w"> </span><span class="n">mode</span><span class="p">;</span> <span class="p">};</span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="w"> </span><span class="n">q</span><span class="p">;</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">SELECT</span><span class="p">});</span> <span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">q</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">Node</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">front</span><span class="p">();</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">100000</span><span class="p">)</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="n">mode</span><span class="p">)</span> <span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">SELECT</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">COPY</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">COPY</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">PASTE</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">PASTE</span><span class="p">:</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">SELECT</span><span class="p">});</span> <span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">({</span><span class="n">n</span><span class="p">.</span><span class="n">noOfAs</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">steps</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">.</span><span class="n">noOfAsCopied</span><span class="p">,</span><span class="w"> </span><span class="n">PASTE</span><span class="p">});</span> <span class="w"> </span><span class="k">break</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div> <p>This is guaranteed to find a shortest possible solution due to a fun property of BFS: the distance of nodes to the origin never decreases. If you evaluate Node Y after Node X, then Y.dist &gt;= X.dist, meaning that the first valid solution will be a shortest possible solution. I should make this into a logic book example! </p> <p>This also has the drawback of preventing <a href="https://www.hillelwayne.com/post/cleverness/" target="_blank">the use of an insight</a>. We <em>should</em> be able to fuse the select and copy steps together, meaning instead of having three actions (select, copy, paste) we only need two (selectcopy, paste), where selectcopy takes twice as many steps as pasting.</p> <p>But we can't make that optimization because it breaks monotonicity. We're now pushing a mix of <code>n+1</code> and <code>n+2</code> steps onto the queue, and there's no way to order things to guarantee all of the <code>n+1</code> steps are searched before any <code>n+2</code> step.</p> <p>I thought I'd try to solve it with planning language instead, so we can get both the elegant solution and the optimization.</p> <h3>Planning</h3> <div class="subscribe-form"></div> <p>The rough idea of planning is that you provide an initial state, a set of actions, and a target, and the tool finds the shortest sequence of actions that reaches the target. I've written about it in-depth <a href="https://www.hillelwayne.com/post/picat/" target="_blank">here</a> and also a comparison of planning to model checking <a href="https://www.hillelwayne.com/post/picat/" target="_blank">here</a>. I like how some tough problems in imperative and functional paradigms become easy problems with planning. </p> <p>This is all in <a href="http://picat-lang.org/" target="_blank">Picat</a>, by the way, which I've talked about more <a href="https://buttondown.email/hillelwayne/archive/picat-is-my-favorite-new-toolbox-language/" target="_blank">here</a> and in the <a href="https://www.hillelwayne.com/post/picat/" target="_blank">planning piece</a>. I'll just be explaining the planning stuff specific to this problem.</p> <div class="codehilite"><pre><span></span><code><span class="s s-Atom">import</span> <span class="s s-Atom">planner</span><span class="p">.</span> <span class="s s-Atom">import</span> <span class="s s-Atom">util</span><span class="p">.</span> <span class="s s-Atom">main</span> <span class="s s-Atom">=&gt;</span> <span class="nv">Init</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">% one a, nothing copied</span> <span class="p">,</span> <span class="nf">best_plan</span><span class="p">(</span><span class="nv">Init</span><span class="p">,</span> <span class="nv">Plan</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="p">,</span> <span class="s s-Atom">nl</span> <span class="p">,</span> <span class="nf">printf</span><span class="p">(</span><span class="s2">"Cost=%d%n"</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="p">,</span> <span class="nf">printf</span><span class="p">(</span><span class="s2">"Plan=%s%n"</span><span class="p">,</span> <span class="nf">join</span><span class="p">([</span><span class="nv">P</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="s s-Atom">:</span> <span class="nv">P</span> <span class="s s-Atom">in</span> <span class="nv">Plan</span><span class="p">],</span> <span class="s2">" "</span><span class="p">))</span> <span class="p">.</span> </code></pre></div> <p>We're storing the state of the system as two integers: the number of characters printed and the number of characters on our clipboard. Since we'll be fusing selects and copies, we don't need to also track the number of characters selected <del>(unlike the C++)</del>.</p> <div class="codehilite"><pre><span></span><code><span class="nf">final</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="k">_</span><span class="p">))</span> <span class="s s-Atom">=&gt;</span> <span class="nv">A</span> <span class="o">&gt;=</span> <span class="mf">100000.</span> <span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">NewA</span> <span class="o">=</span> <span class="nv">A</span> <span class="o">+</span> <span class="nv">Clipboard</span> <span class="p">,</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">NewA</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"P"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">.</span> </code></pre></div> <p>The paste action just adds the clipboard to the character count. Because Picat is a research language it's a little weird with putting expressions inside structures. If we did <code>$state(1 + 1)</code> it would store it as <em>literally</em> <code>$state(1 + 1)</code>, not <code>state(2)</code>.</p> <p>Also you have to use dollar signs for definitions but no dollar signs for pattern matching inside a function definition. I have <em>no idea</em> why.</p> <div class="codehilite"><pre><span></span><code><span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">A</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"SC"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">2</span> <span class="p">.</span> </code></pre></div> <p>And that's it! That's the whole program. Running this gives us:</p> <div class="codehilite"><pre><span></span><code>Cost=42 Plan=SC P P SC P P SC P P SC P P SC P P SC P P SC P P SC P P SC P P P SC P P P </code></pre></div> <p>To find if there's a sequence that gets us <em>exactly</em> 100,000, we just need to make one change:</p> <div class="codehilite"><pre><span></span><code><span class="gd">- final(state(A, _)) =&gt; A &gt;= 100000.</span> <span class="gi">+ final(state(A, _)) =&gt; A = 100000.</span> </code></pre></div> <p>This returns a cost of 43.</p> <p>On the other hand, I can't get it to find a path that makes exactly <code>100,001</code> characters, even with some optimizations. This is because the shortest path is over 9000 steps long! I haven't checked if the C++ BFS can find it.</p> <h3>Metaplanning</h3> <p>One reason planning fascinates me so much is that, if a problem is now easy, you can play around with it. Like if I wanted to add "delete a character" as a move, that's easy:</p> <div class="codehilite"><pre><span></span><code><span class="nf">action</span><span class="p">(</span><span class="nf">state</span><span class="p">(</span><span class="nv">A</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">),</span> <span class="nv">To</span><span class="p">,</span> <span class="nv">Action</span><span class="p">,</span> <span class="nv">Cost</span><span class="p">)</span> <span class="s s-Atom">?=&gt;</span> <span class="nv">A</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">,</span> <span class="nv">NewA</span> <span class="o">=</span> <span class="nv">A</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">,</span> <span class="nv">To</span> <span class="o">=</span> <span class="err">$</span><span class="nf">state</span><span class="p">(</span><span class="nv">NewA</span><span class="p">,</span> <span class="nv">Clipboard</span><span class="p">)</span> <span class="p">,</span> <span class="nv">Action</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"D"</span><span class="p">,</span> <span class="nv">To</span><span class="p">}</span> <span class="p">,</span> <span class="nv">Cost</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">.</span> </code></pre></div> <p>This doesn't make exceeding or reaching 100,000 easier, but it makes reaching 100,001 take 47 steps instead of 9000.</p> <p>With some tweaks, I can also ask questions like "what numbers does it make <em>the most</em> easier?" or "Do some numbers have more than one shortest path? Which number has the most?"</p> <p>Planning is really cool.</p></description><pubDate>Tue, 02 Jul 2024 15:46:13 +0000</pubDate><guid>https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/</guid></item></channel></rss>
{ "cf-cache-status": "DYNAMIC", "cf-ray": "929b459f732afa13-ORD", "connection": "keep-alive", "content-type": "application/rss+xml; charset=utf-8", "cross-origin-opener-policy": "same-origin", "date": "Tue, 01 Apr 2025 21:45:10 GMT", "last-modified": "Tue, 01 Apr 2025 16:04:59 GMT", "nel": "{\"report_to\":\"heroku-nel\",\"response_headers\":[\"Via\"],\"max_age\":3600,\"success_fraction\":0.01,\"failure_fraction\":0.1}", "referrer-policy": "strict-origin-when-cross-origin", "report-to": "{\"group\":\"heroku-nel\",\"endpoints\":[{\"url\":\"https://nel.heroku.com/reports?s=bKK%2Fl97shQmn79banKr8PoPoSNk1xx0P3TY7fTrcqg4%3D\\u0026sid=e11707d5-02a7-43ef-b45e-2cf4d2036f7d\\u0026ts=1743543910\"}],\"max_age\":3600}", "reporting-endpoints": "heroku-nel=\"https://nel.heroku.com/reports?s=bKK%2Fl97shQmn79banKr8PoPoSNk1xx0P3TY7fTrcqg4%3D&sid=e11707d5-02a7-43ef-b45e-2cf4d2036f7d&ts=1743543910\"", "server": "cloudflare", "set-cookie": "initial_path=\"/hillelwayne/rss\"; expires=Thu, 01 May 2025 21:45:10 GMT; Max-Age=2592000; Path=/", "transfer-encoding": "chunked", "vary": "Cookie, Host, origin, Accept-Encoding", "via": "1.1 heroku-router, 1.1 vegur", "x-content-type-options": "nosniff", "x-frame-options": "DENY" }
{ "meta": { "type": "rss", "version": "2.0" }, "language": "en-us", "title": "Computer Things", "description": "Hi, I'm Hillel. This is the newsletter version of [my website](https://www.hillelwayne.com). I post all website updates here. I also post weekly content just for the newsletter, on topics like\n\n* Formal Methods\n\n* Software History and Culture\n\n* Fringetech and exotic tooling\n\n* The philosophy and theory of software engineering\n\nYou can see the archive of all public essays [here](https://buttondown.email/hillelwayne/archive/).", "copyright": null, "url": "https://buttondown.com/hillelwayne", "self": "https://buttondown.email/hillelwayne/rss", "published": null, "updated": "2025-04-01T16:04:59.000Z", "generator": null, "image": null, "authors": [], "categories": [], "items": [ { "id": "https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/", "title": "[April Cools] Gaming Games for Non-Gamers", "description": "<p>My <em>April Cools</em> is out! <a href=\"https://www.hillelwayne.com/post/vidja-games/\" target=\"_blank\">Gaming Games for Non-Gamers</a> is a 3,000 word essay on video games worth playing if you've never enjoyed a video game before. <a href=\"https://www.patreon.com/posts/blog-notes-gamer-125654321?utm_medium=clipboard_copy&utm_source=copyLink&utm_campaign=postshare_creator&utm_content=join_link\" target=\"_blank\">Patreon notes here</a>.</p>\n<p>(April Cools is a project where we write genuine content on non-normal topics. You can see all the other April Cools posted so far <a href=\"https://www.aprilcools.club/\" target=\"_blank\">here</a>. There's still time to submit your own!)</p>\n<a class=\"embedded-link\" href=\"https://www.aprilcools.club/\"> <div style=\"width: 100%; background: #fff; border: 1px #ced3d9 solid; border-radius: 5px; margin-top: 1em; overflow: auto; margin-bottom: 1em;\"> <div style=\"float: left; border-bottom: 1px #ced3d9 solid;\"> <img class=\"link-image\" src=\"https://www.aprilcools.club/aprilcoolsclub.png\"/> </div> <div style=\"float: left; color: #393f48; padding-left: 1em; padding-right: 1em;\"> <h4 class=\"link-title\" style=\"margin-bottom: 0em; line-height: 1.25em; margin-top: 1em; font-size: 14px;\"> April Cools' Club</h4> </div> </div></a>", "url": "https://buttondown.com/hillelwayne/archive/april-cools-gaming-games-for-non-gamers/", "published": "2025-04-01T16:04:59.000Z", "updated": "2025-04-01T16:04:59.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/", "title": "Betteridge's Law of Software Engineering Specialness", "description": "<h3>Logic for Programmers v0.8 now out!</h3>\n<p>The new release has minor changes: new formatting for notes and a better introduction to predicates. I would have rolled it all into v0.9 next month but I like the monthly cadence. <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Get it here!</a></p>\n<h1>Betteridge's Law of Software Engineering Specialness</h1>\n<p>In <a href=\"https://agileotter.blogspot.com/2025/03/there-is-no-automatic-reset-in.html\" target=\"_blank\">There is No Automatic Reset in Engineering</a>, Tim Ottinger asks:</p>\n<blockquote>\n<p>Do the other people have to live with January 2013 for the rest of their lives? Or is it only engineering that has to deal with every dirty hack since the beginning of the organization?</p>\n</blockquote>\n<p><strong>Betteridge's Law of Headlines</strong> says that if a journalism headline ends with a question mark, the answer is probably \"no\". I propose a similar law relating to software engineering specialness:<sup id=\"fnref:ottinger\"><a class=\"footnote-ref\" href=\"#fn:ottinger\">1</a></sup></p>\n<blockquote>\n<p>If someone asks if some aspect of software development is truly unique to just software development, the answer is probably \"no\".</p>\n</blockquote>\n<p>Take the idea that \"in software, hacks are forever.\" My favorite example of this comes from a different profession. The <a href=\"https://en.wikipedia.org/wiki/Dewey_Decimal_Classification\" target=\"_blank\">Dewey Decimal System</a> hierarchically categorizes books by discipline. For example, <em><a href=\"https://www.librarything.com/work/10143437/t/Covered-Bridges-of-Pennsylvania\" target=\"_blank\">Covered Bridges of Pennsylvania</a></em> has Dewey number <code>624.37</code>. <code>6--</code> is the technology discipline, <code>62-</code> is engineering, <code>624</code> is civil engineering, and <code>624.3</code> is \"special types of bridges\". I have no idea what the last <code>0.07</code> means, but you get the picture.</p>\n<p>Now if you look at the <a href=\"https://www.librarything.com/mds/6\" target=\"_blank\">6-- \"technology\" breakdown</a>, you'll see that there's no \"software\" subdiscipline. This is because when Dewey preallocated the whole technology block in 1876. New topics were instead to be added to the <code>00-</code> \"general-knowledge\" catch-all. Eventually <code>005</code> was assigned to \"software development\", meaning <em>The C Programming Language</em> lives at <code>005.133</code>. </p>\n<p>Incidentally, another late addition to the general knowledge block is <code>001.9</code>: \"controversial knowledge\". </p>\n<p>And that's why my hometown library shelved the C++ books right next to <em>The Mothman Prophecies</em>.</p>\n<p>How's <em>that</em> for technical debt?</p>\n<p>If anything, fixing hacks in software is significantly <em>easier</em> than in other fields. This came up when I was <a href=\"https://www.hillelwayne.com/post/we-are-not-special/\" target=\"_blank\">interviewing classic engineers</a>. Kludges happened all the time, but \"refactoring\" them out is <em>expensive</em>. Need to house a machine that's just two inches taller than the room? Guess what, you're cutting a hole in the ceiling.</p>\n<p>(Even if we restrict the question to other departments in a <em>software company</em>, we can find kludges that are horrible to undo. I once worked for a company which landed an early contract by adding a bespoke support agreement for that one customer. That plagued them for years afterward.)</p>\n<p>That's not to say that there aren't things that are different about software vs other fields!<sup id=\"fnref:example\"><a class=\"footnote-ref\" href=\"#fn:example\">2</a></sup> But I think that <em>most</em> of the time, when we say \"software development is the only profession that deals with XYZ\", it's only because we're ignorant of how those other professions work.</p>\n<hr/>\n<p>Short newsletter because I'm way behind on writing my <a href=\"https://www.aprilcools.club/\" target=\"_blank\">April Cools</a>. If you're interested in April Cools, you should try it out! I make it <em>way</em> harder on myself than it actually needs to be— everybody else who participates finds it pretty chill.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:ottinger\">\n<p>Ottinger caveats it with \"engineering, software or otherwise\", so I think he knows that other branches of <em>engineering</em>, at least, have kludges. <a class=\"footnote-backref\" href=\"#fnref:ottinger\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:example\">\n<p>The \"software is different\" idea that I'm most sympathetic to is that in software, the tools we use and the products we create are made from the same material. That's unusual at least in classic engineering. Then again, plenty of machinists have made their own lathes and mills! <a class=\"footnote-backref\" href=\"#fnref:example\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/betteridges-law-of-software-engineering/", "published": "2025-03-26T18:48:39.000Z", "updated": "2025-03-26T18:48:39.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/verification-first-development/", "title": "Verification-First Development", "description": "<p>A while back I argued on the Blue Site<sup id=\"fnref:li\"><a class=\"footnote-ref\" href=\"#fn:li\">1</a></sup> that \"test-first development\" (TFD) was different than \"test-driven development\" (TDD). The former is \"write tests before you write code\", the latter is a paradigm, culture, and collection of norms that's based on TFD. More broadly, TFD is a special case of <strong>Verification-First Development</strong> and TDD is not.</p>\n<blockquote>\n<p>VFD: before writing code, put in place some means of verifying that the code is correct, or at least have an idea of what you'll do.</p>\n</blockquote>\n<p>\"Verifying\" could mean writing tests, or figuring out how to encode invariants in types, or <a href=\"https://blog.regehr.org/archives/1091\" target=\"_blank\">adding contracts</a>, or <a href=\"https://learntla.com/\" target=\"_blank\">making a formal model</a>, or writing a separate script that checks the output of the program. Just have <em>something</em> appropriate in place that you can run as you go building the code. Ideally, we'd have verification in place for every interesting property, but that's rarely possible in practice. </p>\n<p>Oftentimes we can't make the verification until the code is partially complete. In that case it still helps to figure out the verification we'll write later. The point is to have a <em>plan</em> and follow it promptly.</p>\n<p>I'm using \"code\" as a standin for anything we programmers make, not just software programs. When using constraint solvers, I try to find representative problems I know the answers to. When writing formal specifications, I figure out the system's properties before the design that satisfies those properties. There's probably equivalents in security and other topics, too.</p>\n<h3>The Benefits of VFD</h3>\n<ol>\n<li>Doing verification before coding makes it less likely we'll skip verification entirely. It's the professional equivalent of \"No TV until you do your homework.\"</li>\n<li>It's easier to make sure a verifier works properly if we start by running it on code we know doesn't pass it. Bebugging working code takes more discipline.</li>\n<li>We can run checks earlier in the development process. It's better to realize that our code is broken five minutes after we broke it rather than two hours after.</li>\n</ol>\n<p>That's it, those are the benefits of verification-first development. Those are also <em>big</em> benefits for relatively little investment. Specializations of VFD like test-first development can have more benefits, but also more drawbacks.</p>\n<h3>The drawbacks of VFD</h3>\n<ol>\n<li>It slows us down. I know lots of people say that \"no actually it makes you go faster in the long run,\" but that's the <em>long</em> run. Sometimes we do marathons, sometimes we sprint.</li>\n<li>Verification gets in the way of exploratory coding, where we don't know what exactly we want or how exactly to do something.</li>\n<li>Any specific form of verification exerts a pressure on our code to make it easier to verify with that method. For example, if we're mostly verifying via type invariants, we need to figure out how to express those things in our language's type system, which may not be suited for the specific invariants we need.<sup id=\"fnref:sphinx\"><a class=\"footnote-ref\" href=\"#fn:sphinx\">2</a></sup></li>\n</ol>\n<h2>Whether \"pressure\" is a real drawback is incredibly controversial</h2>\n<p>If I had to summarize what makes \"test-driven development\" different from VFD:<sup id=\"fnref:tdd\"><a class=\"footnote-ref\" href=\"#fn:tdd\">3</a></sup></p>\n<ol>\n<li>The form of verification should specifically be tests, and unit tests at that</li>\n<li>Testing pressure is invariably good. \"Making your code easier to unit test\" is the same as \"making your code better\".</li>\n</ol>\n<p>This is something all of the various \"drivens\"— TDD, Type Driven Development, Design by Contract— share in common, this idea that the purpose of the paradigm is to exert pressure. Lots of TDD experts claim that \"having a good test suite\" is only the secondary benefit of TDD and the real benefit is how it improves code quality.<sup id=\"fnref:docs\"><a class=\"footnote-ref\" href=\"#fn:docs\">4</a></sup></p>\n<p>Whether they're right or not is not something I want to argue: I've seen these approaches all improve my code structure, but also sometimes worsen it. Regardless, I consider pressure a drawback to VFD in general, though, for a somewhat idiosyncratic reason. If it <em>weren't</em> for pressure, VFD would be wholly independent of the code itself. It would <em>just</em> be about verification, and our decisions would exclusively be about how we want to verify. But the design pressure means that our means of verification affects the system we're checking. What if these conflict in some way?</p>\n<h3>VFD is a technique, not a paradigm</h3>\n<p>One of the main differences between \"techniques\" and \"paradigms\" is that paradigms don't play well with each other. If you tried to do both \"proper\" Test-Driven Development and \"proper\" Cleanroom, your head would explode. Whereas VFD being a \"technique\" means it works well with other techniques and even with many full paradigms.</p>\n<p>It also doesn't take a whole lot of practice to start using. It does take practice, both in thinking of verifications and in using the particular verification method involved, to <em>use well</em>, but we can use it poorly and still benefit.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:li\">\n<p>LinkedIn, what did you think I meant? <a class=\"footnote-backref\" href=\"#fnref:li\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:sphinx\">\n<p>This bit me in the butt when making my own <a href=\"https://www.sphinx-doc.org/en/master/\" target=\"_blank\">sphinx</a> extensions. The official guides do things in a highly dynamic way that Mypy can't statically check. I had to do things in a completely different way. Ended up being better though! <a class=\"footnote-backref\" href=\"#fnref:sphinx\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:tdd\">\n<p>Someone's going to yell at me that I completely missed the point of TDD, which is XYZ. Well guess what, someone else <em>already</em> yelled at me that only dumb idiot babies think XYZ is important in TDD. Put in whatever you want for XYZ. <a class=\"footnote-backref\" href=\"#fnref:tdd\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n<li id=\"fn:docs\">\n<p>Another thing that weirdly all of the paradigms claim: that they lead to better documentation. I can see the argument, I just find it strange that <em>every single one</em> makes this claim! <a class=\"footnote-backref\" href=\"#fnref:docs\" title=\"Jump back to footnote 4 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/verification-first-development/", "published": "2025-03-18T16:22:20.000Z", "updated": "2025-03-18T16:22:20.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/", "title": "New Blog Post: \"A Perplexing Javascript Parsing Puzzle\"", "description": "<p>I know I said we'd be back to normal newsletters this week and in fact had 80% of one already written. </p>\n<p>Then I unearthed something that was better left buried.</p>\n<p><a href=\"http://www.hillelwayne.com/post/javascript-puzzle/\" target=\"_blank\">Blog post here</a>, <a href=\"https://www.patreon.com/posts/blog-notes-124153641\" target=\"_blank\">Patreon notes here</a> (Mostly an explanation of how I found this horror in the first place). Next week I'll send what was supposed to be this week's piece.</p>\n<p>(PS: <a href=\"https://www.aprilcools.club/\" target=\"_blank\">April Cools</a> in three weeks!)</p>", "url": "https://buttondown.com/hillelwayne/archive/new-blog-post-a-perplexing-javascript-parsing/", "published": "2025-03-12T14:49:52.000Z", "updated": "2025-03-12T14:49:52.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/", "title": "Five Kinds of Nondeterminism", "description": "<p>No newsletter next week, I'm teaching a TLA+ workshop.</p>\n<p>Speaking of which: I spend a lot of time thinking about formal methods (and TLA+ specifically) because it's where the source of almost all my revenue. But I don't share most of the details because 90% of my readers don't use FM and never will. I think it's more interesting to talk about ideas <em>from</em> FM that would be useful to people outside that field. For example, the idea of \"property strength\" translates to the <a href=\"https://buttondown.com/hillelwayne/archive/some-tests-are-stronger-than-others/\" target=\"_blank\">idea that some tests are stronger than others</a>. </p>\n<p>Another possible export is how FM approaches nondeterminism. A <strong>nondeterministic</strong> algorithm is one that, from the same starting conditions, has multiple possible outputs. This is nondeterministic:</p>\n<div class=\"codehilite\"><pre><span></span><code># Pseudocode\n\ndef f() {\n return rand()+1;\n}\n</code></pre></div>\n<p>When specifying systems, I may not <em>encounter</em> nondeterminism more often than in real systems, but I am definitely more aware of its presence. Modeling nondeterminism is a core part of formal specification. I mentally categorize nondeterminism into five buckets. Caveat, this is specifically about nondeterminism from the perspective of <em>system modeling</em>, not computer science as a whole. If I tried to include stuff on NFAs and amb operations this would be twice as long.<sup id=\"fnref:nondeterminism\"><a class=\"footnote-ref\" href=\"#fn:nondeterminism\">1</a></sup></p>\n<p style=\"height:16px; margin:0px !important;\"></p>\n<h2>1. True Randomness</h2>\n<p>Programs that literally make calls to a <code>random</code> function and then use the results. This the simplest type of nondeterminism and one of the most ubiquitous. </p>\n<p>Most of the time, <code>random</code> isn't <em>truly</em> nondeterministic. Most of the time computer randomness is actually <strong>pseudorandom</strong>, meaning we seed a deterministic algorithm that behaves \"randomly-enough\" for some use. You could \"lift\" a nondeterministic random function into a deterministic one by adding a fixed seed to the starting state.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\"># Python</span>\n\n<span class=\"kn\">from</span> <span class=\"nn\">random</span> <span class=\"kn\">import</span> <span class=\"n\">random</span><span class=\"p\">,</span> <span class=\"n\">seed</span>\n<span class=\"k\">def</span> <span class=\"nf\">f</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">):</span>\n <span class=\"n\">seed</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">random</span><span class=\"p\">()</span>\n\n<span class=\"o\">>>></span> <span class=\"n\">f</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">)</span>\n<span class=\"mf\">0.23796462709189137</span>\n<span class=\"o\">>>></span> <span class=\"n\">f</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">)</span>\n<span class=\"mf\">0.23796462709189137</span>\n</code></pre></div>\n<p>Often we don't do this because the <em>point</em> of randomness is to provide nondeterminism! We deliberately <em>abstract out</em> the starting state of the seed from our program, because it's easier to think about it as locally nondeterministic.</p>\n<p>(There's also \"true\" randomness, like using <a href=\"https://www.intel.com/content/www/us/en/developer/articles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html#inpage-nav-3-2\" target=\"_blank\">thermal noise</a> as an entropy source, which I think are mainly used for cryptography and seeding PRNGs.)</p>\n<p>Most formal specification languages don't deal with randomness (though some deal with <a href=\"https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/\" target=\"_blank\">probability more broadly</a>). Instead, we treat it as a nondeterministic choice:</p>\n<div class=\"codehilite\"><pre><span></span><code># software\nif rand > 0.001 then return a else crash\n\n# specification\neither return a or crash\n</code></pre></div>\n<p>This is because we're looking at worst-case scenarios, so it doesn't matter if <code>crash</code> happens 50% of the time or 0.0001% of the time, it's still possible. </p>\n<h2>2. Concurrency</h2>\n<div class=\"codehilite\"><pre><span></span><code># Pseudocode\nglobal x = 1, y = 0;\n\ndef thread1() {\n x++;\n x++;\n x++;\n}\n\ndef thread2() {\n y := x;\n}\n</code></pre></div>\n<p>If <code>thread1()</code> and <code>thread2()</code> run sequentially, then (assuming the sequence is fixed) the final value of <code>y</code> is deterministic. If the two functions are started and run simultaneously, then depending on when <code>thread2</code> executes <code>y</code> can be 1, 2, 3, <em>or</em> 4. Both functions are locally sequential, but running them concurrently leads to global nondeterminism.</p>\n<p>Concurrency is arguably the most <em>dramatic</em> source of nondeterminism. <a href=\"https://buttondown.com/hillelwayne/archive/what-makes-concurrency-so-hard/\" target=\"_blank\">Small amounts of concurrency lead to huge explosions in the state space</a>. We have words for the specific kinds of nondeterminism caused by concurrency, like \"race condition\" and \"dirty write\". Often we think about it as a separate <em>topic</em> from nondeterminism. To some extent it \"overshadows\" the other kinds: I have a much easier time teaching students about concurrency in models than nondeterminism in models.</p>\n<p>Many formal specification languages have special syntax/machinery for the concurrent aspects of a system, and generic syntax for other kinds of nondeterminism. In P that's <a href=\"https://p-org.github.io/P/manual/expressions/#choose\" target=\"_blank\">choose</a>. Others don't special-case concurrency, instead representing as it as nondeterministic choices by a global coordinator. This more flexible but also more inconvenient, as you have to implement process-local sequencing code yourself. </p>\n<h2>3. User Input</h2>\n<div class=\"subscribe-form\"></div>\n<p>One of the most famous and influential programming books is <em>The C Programming Language</em> by Kernighan and Ritchie. The first example of a nondeterministic program appears on page 14:</p>\n<p><img alt=\"Picture of the book page. Code reproduced below.\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/94e6ad15-8d09-48df-b885-191318bfd179.jpg?w=960&fit=max\"/></p>\n<p>For the newsletter readers who get text only emails,<sup id=\"fnref:text-only\"><a class=\"footnote-ref\" href=\"#fn:text-only\">2</a></sup> here's the program:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"cp\">#include</span><span class=\"w\"> </span><span class=\"cpf\"><stdio.h></span>\n<span class=\"cm\">/* copy input to output; 1st version */</span>\n<span class=\"n\">main</span><span class=\"p\">()</span>\n<span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"n\">c</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">getchar</span><span class=\"p\">();</span>\n<span class=\"w\"> </span><span class=\"k\">while</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">c</span><span class=\"w\"> </span><span class=\"o\">!=</span><span class=\"w\"> </span><span class=\"n\">EOF</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"n\">putchar</span><span class=\"p\">(</span><span class=\"n\">c</span><span class=\"p\">);</span>\n<span class=\"w\"> </span><span class=\"n\">c</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">getchar</span><span class=\"p\">();</span>\n<span class=\"w\"> </span><span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p>Yup, that's nondeterministic. Because the user can enter any string, any call of <code>main()</code> could have any output, meaning the number of possible outcomes is infinity.</p>\n<p>Okay that seems a little cheap, and I think it's because we tend to think of determinism in terms of how the user <em>experiences</em> the program. Yes, <code>main()</code> has an infinite number of user inputs, but for each input the user will experience only one possible output. It starts to feel more nondeterministic when modeling a long-standing system that's <em>reacting</em> to user input, for example a server that runs a script whenever the user uploads a file. This can be modeled with nondeterminism and concurrency: We have one execution that's the system, and one nondeterministic execution that represents the effects of our user.</p>\n<p>(One intrusive thought I sometimes have: any \"yes/no\" dialogue actually has <em>three</em> outcomes: yes, no, or the user getting up and walking away without picking a choice, permanently stalling the execution.)</p>\n<h2>4. External forces</h2>\n<p>The more general version of \"user input\": anything where either 1) some part of the execution outcome depends on retrieving external information, or 2) the external world can change some state outside of your system. I call the distinction between internal and external components of the system <a href=\"https://www.hillelwayne.com/post/world-vs-machine/\" target=\"_blank\">the world and the machine</a>. Simple examples: code that at some point reads an external temperature sensor. Unrelated code running on a system which quits programs if it gets too hot. API requests to a third party vendor. Code processing files but users can delete files before the script gets to them.</p>\n<p>Like with PRNGs, some of these cases don't <em>have</em> to be nondeterministic; we can argue that \"the temperature\" should be a virtual input into the function. Like with PRNGs, we treat it as nondeterministic because it's useful to think in that way. Also, what if the temperature changes between starting a function and reading it?</p>\n<p>External forces are also a source of nondeterminism as <em>uncertainty</em>. Measurements in the real world often comes with errors, so repeating a measurement twice can give two different answers. Sometimes operations fail for no discernable reason, or for a non-programmatic reason (like something physically blocks the sensor).</p>\n<p>All of these situations can be modeled in the same way as user input: a concurrent execution making nondeterministic choices.</p>\n<h2>5. Abstraction</h2>\n<p>This is where nondeterminism in system models and in \"real software\" differ the most. I said earlier that pseudorandomness is <em>arguably</em> deterministic, but we abstract it into nondeterminism. More generally, <strong>nondeterminism hides implementation details of deterministic processes</strong>.</p>\n<p>In one consulting project, we had a machine that received a message, parsed a lot of data from the message, went into a complicated workflow, and then entered one of three states. The final state was totally deterministic on the content of the message, but the actual process of determining that final state took tons and tons of code. None of that mattered at the scope we were modeling, so we abstracted it all away: \"on receiving message, nondeterministically enter state A, B, or C.\"</p>\n<p>Doing this makes the system easier to model. It also makes the model more sensitive to possible errors. What if the workflow is bugged and sends us to the wrong state? That's already covered by the nondeterministic choice! Nondeterministic abstraction gives us the potential to pick the worst-case scenario for our system, so we can prove it's robust even under those conditions.</p>\n<p>I know I beat the \"nondeterminism as abstraction\" drum a whole lot but that's because it's the insight from formal methods I personally value the most, that nondeterminism is a powerful tool to <em>simplify reasoning about things</em>. You can see the same approach in how I approach modeling users and external forces: complex realities black-boxed and simplified into nondeterministic forces on the system.</p>\n<hr/>\n<p>Anyway, I hope this collection of ideas I got from formal methods are useful to my broader readership. Lemme know if it somehow helps you out!</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:nondeterminism\">\n<p>I realized after writing this that I already talked wrote an essay about nondeterminism in formal specification <a href=\"https://buttondown.com/hillelwayne/archive/nondeterminism-in-formal-specification/\" target=\"_blank\">just under a year ago</a>. I hope this one covers enough new ground to be interesting! <a class=\"footnote-backref\" href=\"#fnref:nondeterminism\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:text-only\">\n<p>There is a surprising number of you. <a class=\"footnote-backref\" href=\"#fnref:text-only\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/five-kinds-of-nondeterminism/", "published": "2025-02-19T19:37:57.000Z", "updated": "2025-02-19T19:37:57.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/", "title": "Are Efficiency and Horizontal Scalability at odds?", "description": "<p>Sorry for missing the newsletter last week! I started writing on Monday as normal, and by Wednesday the piece (about the <a href=\"https://en.wikipedia.org/wiki/Hierarchy_of_hazard_controls\" target=\"_blank\">hierarchy of controls</a> ) was 2000 words and not <em>close</em> to done. So now it'll be a blog post sometime later this month.</p>\n<p>I also just released a new version of <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Logic for Programmers</a>! 0.7 adds a bunch of new content (type invariants, modeling access policies, rewrites of the first chapters) but more importantly has new fonts that are more legible than the old ones. <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Go check it out!</a></p>\n<p>For this week's newsletter I want to brainstorm an idea I've been noodling over for a while. Say we have a computational task, like running a simulation or searching a very large graph, and it's taking too long to complete on a computer. There's generally three things that we can do to make it faster:</p>\n<ol>\n<li>Buy a faster computer (\"vertical scaling\")</li>\n<li>Modify the software to use the computer's resources better (\"efficiency\")</li>\n<li>Modify the software to use multiple computers (\"horizontal scaling\")</li>\n</ol>\n<p>(Splitting single-threaded software across multiple threads/processes is sort of a blend of (2) and (3).)</p>\n<p>The big benefit of (1) is that we (usually) don't have to make any changes to the software to get a speedup. The downside is that for the past couple of decades computers haven't <em>gotten</em> much faster, except in ways that require recoding (like GPUs and multicore). This means we rely on (2) and (3), and we can do both to a point. I've noticed, though, that horizontal scaling seems to conflict with efficiency. Software optimized to scale well tends to be worse or the <code>N=1</code> case than software optimized to, um, be optimized. </p>\n<p>Are there reasons to <em>expect</em> this? It seems reasonable that design goals of software are generally in conflict, purely because exclusively optimizing for one property means making decisions that impede other properties. But is there something in the nature of \"efficiency\" and \"horizontal scalability\" that make them especially disjoint?</p>\n<p>This isn't me trying to explain a fully coherent idea, more me trying to figure this all out to myself. Also I'm probably getting some hardware stuff wrong</p>\n<h3>Amdahl's Law</h3>\n<p>According to <a href=\"https://en.wikipedia.org/wiki/Amdahl%27s_law\" target=\"_blank\">Amdahl's Law</a>, the maximum speedup by parallelization is constrained by the proportion of the work that can be parallelized. If 80% of algorithm X is parallelizable, the maximum speedup from horizontal scaling is 5x. If algorithm Y is 25% parallelizable, the maximum speedup is only 1.3x. </p>\n<p>If you need horizontal scalability, you want to use algorithm X, <em>even if Y is naturally 3x faster</em>. But if Y was 4x faster, you'd prefer it to X. Maximal scalability means finding the optimal balance between baseline speed and parallelizability. Maximal efficiency means just optimizing baseline speed. </p>\n<h3>Coordination Overhead</h3>\n<p>Distributed algorithms require more coordination. To add a list of numbers in parallel via <a href=\"https://en.wikipedia.org/wiki/Fork%E2%80%93join_model\" target=\"_blank\">fork-join</a>, we'd do something like this:</p>\n<ol>\n<li>Split the list into N sublists</li>\n<li>Fork a new thread/process for sublist</li>\n<li>Wait for each thread/process to finish</li>\n<li>Add the sums together.</li>\n</ol>\n<p>(1), (2), and (3) all add overhead to the algorithm. At the very least, it's extra lines of code to execute, but it can also mean inter-process communication or network hops. Distribution also means you have fewer natural correctness guarantees, so you need more administrative overhead to avoid race conditions. </p>\n<p><strong>Real world example:</strong> Historically CPython has a \"global interpreter lock\" (GIL). In multithreaded code, only one thread could execute Python code at a time (others could execute C code). The <a href=\"https://docs.python.org/3/howto/free-threading-python.html#single-threaded-performance\" target=\"_blank\">newest version</a> supports disabling the GIL, which comes at a 40% overhead for single-threaded programs. Supposedly the difference is because the <a href=\"https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep659\" target=\"_blank\">specializing adaptor</a> optimization isn't thread-safe yet. The Python team is hoping on getting it down to \"only\" 10%. </p>\n<p style=\"height:16px; margin:0px !important;\"></p>\n<h3>Scaling loses shared resources</h3>\n<p>I'd say that intra-machine scaling (multiple threads/processes) feels qualitatively <em>different</em> than inter-machine scaling. Part of that is that intra-machine scaling is \"capped\" while inter-machine is not. But there's also a difference in what assumptions you can make about shared resources. Starting from the baseline of single-threaded program:</p>\n<ol>\n<li>Threads have a much harder time sharing CPU caches (you have to manually mess with affinities)</li>\n<li>Processes have a much harder time sharing RAM (I think you have to use <a href=\"https://en.wikipedia.org/wiki/Memory-mapped_file\" target=\"_blank\">mmap</a>?)</li>\n<li>Machines can't share cache, RAM, or disk, period.</li>\n</ol>\n<p>It's a lot easier to solve a problem when the whole thing fits in RAM. But if you split a 50 gb problem across three machines, it doesn't fit in ram by default, even if the machines have 64 gb each. Scaling also means that separate machines can't reuse resources like database connections.</p>\n<h3>Efficiency comes from limits</h3>\n<p>I think the two previous points tie together in the idea that maximal efficiency comes from being able to make assumptions about the system. If we know the <em>exact</em> sequence of computations, we can aim to minimize cache misses. If we don't have to worry about thread-safety, <a href=\"https://www.playingwithpointers.com/blog/refcounting-harder-than-it-sounds.html\" target=\"_blank\">tracking references is dramatically simpler</a>. If we have all of the data in a single database, our query planner has more room to work with. At various tiers of scaling these assumptions are no longer guaranteed and we lose the corresponding optimizations.</p>\n<p>Sometimes these assumptions are implicit and crop up in odd places. Like if you're working at a scale where you need multiple synced databases, you might want to use UUIDs instead of numbers for keys. But then you lose the assumption \"recently inserted rows are close together in the index\", which I've read <a href=\"https://www.cybertec-postgresql.com/en/unexpected-downsides-of-uuid-keys-in-postgresql/\" target=\"_blank\">can lead to significant slowdowns</a>. </p>\n<p>This suggests that if you can find a limit somewhere else, you can get both high horizontal scaling and high efficiency. <del>Supposedly the <a href=\"https://tigerbeetle.com/\" target=\"_blank\">TigerBeetle database</a> has both, but that could be because they limit all records to <a href=\"https://docs.tigerbeetle.com/coding/\" target=\"_blank\">accounts and transfers</a>. This means every record fits in <a href=\"https://tigerbeetle.com/blog/2024-07-23-rediscovering-transaction-processing-from-history-and-first-principles/#transaction-processing-from-first-principles\" target=\"_blank\">exactly 128 bytes</a>.</del> [A TigerBeetle engineer reached out to tell me that they do <em>not</em> horizontally scale compute, they distribute across multiple nodes for redundancy. <a href=\"https://lobste.rs/s/5akiq3/are_efficiency_horizontal_scalability#c_ve8ud5\" target=\"_blank\">\"You can't make it faster by adding more machines.\"</a>]</p>\n<p>Does this mean that \"assumptions\" could be both \"assumptions about the computing environment\" and \"assumptions about the problem\"? In the famous essay <a href=\"http://www.frankmcsherry.org/graph/scalability/cost/2015/01/15/COST.html\" target=\"_blank\">Scalability! But at what COST</a>, Frank McSherry shows that his single-threaded laptop could outperform 128-node \"big data systems\" on PageRank and graph connectivity (via label propagation). Afterwards, he discusses how a different algorithm solves graph connectivity even faster: </p>\n<blockquote>\n<p>[Union find] is more line of code than label propagation, but it is 10x faster and 100x less embarassing. … The union-find algorithm is fundamentally incompatible with the graph computation approaches Giraph, GraphLab, and GraphX put forward (the so-called “think like a vertex” model).</p>\n</blockquote>\n<p>The interesting thing to me is that his alternate makes more \"assumptions\" than what he's comparing to. He can \"assume\" a fixed goal and optimize the code for that goal. The \"big data systems\" are trying to be general purpose compute platforms and have to pick a model that supports the widest range of possible problems. </p>\n<p>A few years back I wrote <a href=\"https://www.hillelwayne.com/post/cleverness/\" target=\"_blank\">clever vs insightful code</a>, I think what I'm trying to say here is that efficiency comes from having insight into your problem and environment.</p>\n<p>(Last thought to shove in here: to exploit assumptions, you need <em>control</em>. Carefully arranging your data to fit in L1 doesn't matter if your programming language doesn't let you control where things are stored!)</p>\n<h3>Is there a cultural aspect?</h3>\n<p>Maybe there's also a cultural element to this conflict. What if the engineers interested in \"efficiency\" are different from the engineers interested in \"horizontal scaling\"?</p>\n<p>At my first job the data scientists set up a <a href=\"https://en.wikipedia.org/wiki/Apache_Hadoop\" target=\"_blank\">Hadoop</a> cluster for their relatively small dataset, only a few dozen gigabytes or so. One of the senior software engineers saw this and said \"big data is stupid.\" To prove it, he took one of their example queries, wrote a script in Go to compute the same thing, and optimized it to run faster on his machine.</p>\n<p>At the time I was like \"yeah, you're right, big data IS stupid!\" But I think now that we both missed something obvious: with the \"scalable\" solution, the data scientists <em>didn't</em> have to write an optimized script for every single query. Optimizing code is hard, adding more machines is easy! </p>\n<p>The highest-tier of horizontal scaling is usually something large businesses want, and large businesses like problems that can be solved purely with money. Maximizing efficiency requires a lot of knowledge-intensive human labour, so is less appealing as an investment. Then again, I've seen a lot of work on making the scalable systems more efficient, such as evenly balancing heterogeneous workloads. Maybe in the largest systems intra-machine efficiency is just too small-scale a problem. </p>\n<h3>I'm not sure where this fits in but scaling a volume of tasks conflicts less than scaling individual tasks</h3>\n<p>If you have 1,000 machines and need to crunch one big graph, you probably want the most scalable algorithm. If you instead have 50,000 small graphs, you probably want the most efficient algorithm, which you then run on all 1,000 machines. When we call a problem <a href=\"https://en.wikipedia.org/wiki/Embarrassingly_parallel\" target=\"_blank\">embarrassingly parallel</a>, we usually mean it's easy to horizontally scale. But it's also one that's easy to make more efficient, because local optimizations don't affect the scaling! </p>\n<hr/>\n<p>Okay that's enough brainstorming for one week.</p>\n<h3>Blog Rec</h3>\n<p>Whenever I think about optimization as a skill, the first article that comes to mind is <a href=\"https://matklad.github.io/\" target=\"_blank\">Mat Klad's</a> <a href=\"https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html\" target=\"_blank\">Push Ifs Up And Fors Down</a>. I'd never have considered on my own that inlining loops into functions could be such a huge performance win. The blog has a lot of other posts on the nuts-and-bolts of systems languages, optimization, and concurrency.</p>", "url": "https://buttondown.com/hillelwayne/archive/are-efficiency-and-horizontal-scalability-at-odds/", "published": "2025-02-12T18:26:20.000Z", "updated": "2025-02-12T18:26:20.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/", "title": "What hard thing does your tech make easy?", "description": "<p>I occasionally receive emails asking me to look at the writer's new language/library/tool. Sometimes it's in an area I know well, like formal methods. Other times, I'm a complete stranger to the field. Regardless, I'm generally happy to check it out.</p>\n<p>When starting out, this is the biggest question I'm looking to answer:</p>\n<blockquote>\n<p>What does this technology make easy that's normally hard?</p>\n</blockquote>\n<p>What justifies me learning and migrating to a <em>new</em> thing as opposed to fighting through my problems with the tools I already know? The new thing has to have some sort of value proposition, which could be something like \"better performance\" or \"more secure\". The most universal value and the most direct to show is \"takes less time and mental effort to do something\". I can't accurately judge two benchmarks, but I can see two demos or code samples and compare which one feels easier to me.</p>\n<h2>Examples</h2>\n<h3>Functional programming</h3>\n<p>What drew me originally to functional programming was higher order functions. </p>\n<div class=\"codehilite\"><pre><span></span><code># Without HOFs\n\nout = []\nfor x in input {\n if test(x) {\n out.append(x)\n }\n}\n\n# With HOFs\n\nfilter(test, input)\n</code></pre></div>\n<p style=\"height:16px; margin:0px !important;\"></p>\n<p>We can also compare the easiness of various tasks between examples within the same paradigm. If I know FP via Clojure, what could be appealing about Haskell or F#? For one, null safety is a lot easier when I've got option types.</p>\n<h3>Array Programming</h3>\n<p>Array programming languages like APL or J make certain classes of computation easier. For example, finding all of the indices where two arrays <del>differ</del> match. Here it is in Python:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">]</span>\n<span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">]</span>\n\n<span class=\"o\">>>></span> <span class=\"p\">[</span><span class=\"n\">i</span> <span class=\"k\">for</span> <span class=\"n\">i</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span> <span class=\"ow\">in</span> <span class=\"nb\">enumerate</span><span class=\"p\">(</span><span class=\"nb\">zip</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">))</span> <span class=\"k\">if</span> <span class=\"n\">a</span> <span class=\"o\">==</span> <span class=\"n\">b</span><span class=\"p\">]</span>\n<span class=\"p\">[</span><span class=\"mi\">7</span><span class=\"p\">,</span> <span class=\"mi\">9</span><span class=\"p\">]</span>\n</code></pre></div>\n<p>And here it is in J:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"w\"> </span><span class=\"nv\">x</span><span class=\"w\"> </span><span class=\"o\">=:</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">4</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"mi\">3</span><span class=\"w\"> </span><span class=\"mi\">4</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"w\"> </span><span class=\"mi\">4</span>\n<span class=\"w\"> </span><span class=\"nv\">y</span><span class=\"w\"> </span><span class=\"o\">=:</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"mi\">3</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"mi\">3</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"mi\">4</span>\n\n<span class=\"w\"> </span><span class=\"nv\">I</span><span class=\"o\">.</span><span class=\"w\"> </span><span class=\"nv\">x</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nv\">y</span>\n<span class=\"mi\">7</span><span class=\"w\"> </span><span class=\"mi\">9</span>\n</code></pre></div>\n<p>Not every tool is meant for every programmer, because you might not have any of the problems a tool makes easier. What comes up more often for you: filtering a list or finding all the indices where two lists differ? Statistically speaking, functional programming is more useful to you than array programming.</p>\n<p>But <em>I</em> have this problem enough to justify learning array programming.</p>\n<h3>LLMs</h3>\n<p>I think a lot of the appeal of LLMs is they make a lot of specialist tasks easy for nonspecialists. One thing I recently did was convert some rst <a href=\"https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table\" target=\"_blank\">list tables</a> to <a href=\"https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1\" target=\"_blank\">csv tables</a>. Normally I'd have to do write some tricky parsing and serialization code to automatically convert between the two. With LLMs, it's just</p>\n<blockquote>\n<p>Convert the following rst list-table into a csv-table: [table]</p>\n</blockquote>\n<p>\"Easy\" can trump \"correct\" as a value. The LLM might get some translations wrong, but it's so convenient I'd rather manually review all the translations for errors than write specialized script that is correct 100% of the time.</p>\n<h2>Let's not take this too far</h2>\n<p>A college friend once claimed that he cracked the secret of human behavior: humans do whatever makes them happiest. \"What about the martyr who dies for their beliefs?\" \"Well, in their last second of life they get REALLY happy.\"</p>\n<p>We can do the same here, fitting every value proposition into the frame of \"easy\". CUDA makes it easier to do matrix multiplication. Rust makes it easier to write low-level code without memory bugs. TLA+ makes it easier to find errors in your design. Monads make it easier to sequence computations in a lazy environment. Making everything about \"easy\" obscures other reason for adopting new things.</p>\n<h3>That whole \"simple vs easy\" thing</h3>\n<p>Sometimes people think that \"simple\" is better than \"easy\", because \"simple\" is objective and \"easy\" is subjective. This comes from the famous talk <a href=\"https://www.infoq.com/presentations/Simple-Made-Easy/\" target=\"_blank\">Simple Made Easy</a>. I'm not sure I agree that simple is better <em>or</em> more objective: the speaker claims that polymorphism and typeclasses are \"simpler\" than conditionals, and I doubt everybody would agree with that.</p>\n<p>The problem is that \"simple\" is used to mean both \"not complicated\" <em>and</em> \"not complex\". And everybody agrees that \"complicated\" and \"complex\" are different, even if they can't agree <em>what</em> the difference is. This idea should probably expanded be expanded into its own newsletter.</p>\n<p>It's also a lot harder to pitch a technology on being \"simpler\". Simplicity by itself doesn't make a tool better equipped to solve problems. Simplicity can unlock other benefits, like compositionality or <a href=\"https://buttondown.com/hillelwayne/archive/the-capability-tractability-tradeoff/\" target=\"_blank\">tractability</a>, that provide the actual value. And often that value is in the form of \"makes some tasks easier\". </p>", "url": "https://buttondown.com/hillelwayne/archive/what-hard-thing-does-your-tech-make-easy/", "published": "2025-01-29T18:09:47.000Z", "updated": "2025-01-29T18:09:47.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/the-jugglers-curse/", "title": "The Juggler's Curse", "description": "<p>I'm making a more focused effort to juggle this year. Mostly <a href=\"https://youtu.be/PPhG_90VH5k?si=AxOO65PcX4ZwnxPQ&t=49\" target=\"_blank\">boxes</a>, but also classic balls too.<sup id=\"fnref:boxes\"><a class=\"footnote-ref\" href=\"#fn:boxes\">1</a></sup> I've gotten to the point where I can almost consistently do a five-ball cascade, which I <em>thought</em> was the cutoff to being a \"good juggler\". \"Thought\" because I now know a \"good juggler\" is one who can do the five-ball cascade with <em>outside throws</em>. </p>\n<p>I know this because I can't do the outside five-ball cascade... yet. But it's something I can see myself eventually mastering, unlike the slightly more difficult trick of the five-ball mess, which is impossible for mere mortals like me. </p>\n<p><em>In theory</em> there is a spectrum of trick difficulties and skill levels. I could place myself on the axis like this:</p>\n<p><img alt=\"A crudely-drawn scale with 10 even ticks, I'm between 5 and 6\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/8ee51aa1-5dd4-48b8-8110-2cdf9a273612.png?w=960&fit=max\"/></p>\n<p>In practice, there are three tiers:</p>\n<ol>\n<li>Toddlers</li>\n<li>Good jugglers who practice hard</li>\n<li>Genetic freaks and actual wizards</li>\n</ol>\n<p>And the graph always, <em>always</em> looks like this:</p>\n<p><img alt=\"The same graph, with the top compressed into \"wizards\" and bottom into \"toddlers\". I'm in toddlers.\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/04c76cec-671e-4560-b64e-498b7652359e.png?w=960&fit=max\"/></p>\n<p>This is the jugglers curse, and it's a three-parter:</p>\n<ol>\n<li>The threshold between you and \"good\" is the next trick you cannot do.</li>\n<li>Everything below that level is trivial. Once you've gotten a trick down, you can never go back to not knowing it, to appreciating how difficult it was to learn in the first place.<sup id=\"fnref:expert-blindness\"><a class=\"footnote-ref\" href=\"#fn:expert-blindness\">2</a></sup></li>\n<li>Everything above that level is just \"impossible\". You don't have the knowledge needed to recognize the different tiers.<sup id=\"fnref:dk\"><a class=\"footnote-ref\" href=\"#fn:dk\">3</a></sup></li>\n</ol>\n<p>So as you get better, the stuff that was impossible becomes differentiable, and you can see that some of it <em>is</em> possible. And everything you learned becomes trivial. So you're never a good juggler until you learn \"just one more hard trick\".</p>\n<p>The more you know, the more you know you don't know and the less you know you know.</p>\n<h3>This is supposed to be a software newsletter</h3>\n<blockquote>\n<p>A monad is a monoid in the category of endofunctors, what's the problem? <a href=\"https://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html\" target=\"_blank\">(src)</a></p>\n</blockquote>\n<p>I think this applies to any difficult topic? Most fields don't have the same stark <a href=\"https://en.wikipedia.org/wiki/Spectral_line\" target=\"_blank\">spectral lines</a> as juggling, but there's still tiers of difficulty to techniques, which get compressed the further in either direction they are from your current level.</p>\n<p>Like, I'm not good at formal methods. I've written two books on it but I've never mastered a dependently-typed language or a theorem prover. Those are equally hard. And I'm not good at modeling concurrent systems because I don't understand the formal definition of bisimulation and haven't implemented a Raft. Those are also equally hard, in fact exactly as hard as mastering a theorem prover.</p>\n<p>At the same time, the skills I've already developed are easy: properly using refinement is <em>exactly as easy</em> as writing <a href=\"https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/\" target=\"_blank\">a wrapped counter</a>. Then I get surprised when I try to explain strong fairness to someone and they just don't get how □◇(ENABLED〈A〉ᵥ) is <em>obviously</em> different from ◇□(ENABLED 〈A〉ᵥ).</p>\n<p>Juggler's curse!</p>\n<p>Now I don't actually know if this is actually how everybody experiences expertise or if it's just my particular personality— I was a juggler long before I was a software developer. Then again, I'd argue that lots of people talk about one consequence of the juggler's curse: imposter syndrome. If you constantly think what you know is \"trivial\" and what you don't know is \"impossible\", then yeah, you'd start feeling like an imposter at work real quick.</p>\n<p>I wonder if part of the cause is that a lot of skills you have to learn are invisible. One of my favorite blog posts ever is <a href=\"https://www.benkuhn.net/blub/\" target=\"_blank\">In Defense of Blub Studies</a>, which argues that software expertise comes through understanding \"boring\" topics like \"what all of the error messages mean\" and \"how to use a debugger well\". Blub is a critical part of expertise and takes a lot of hard work to learn, but it <em>feels</em> like trivia. So looking back on a skill I mastered, I might think it was \"easy\" because I'm not including all of the blub that I had to learn, too.</p>\n<p>The takeaway, of course, is that the outside five-ball cascade <em>is</em> objectively the cutoff between good jugglers and toddlers.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:boxes\">\n<p>Rant time: I <em>love</em> cigar box juggling. It's fun, it's creative, it's totally unlike any other kind of juggling. And it's so niche I straight up cannot find anybody in Chicago to practice with. I once went to a juggling convention and was the only person with a cigar box set there. <a class=\"footnote-backref\" href=\"#fnref:boxes\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:expert-blindness\">\n<p>This particular part of the juggler's curse is also called <a href=\"https://en.wikipedia.org/wiki/Curse_of_knowledge\" target=\"_blank\">the curse of knowledge</a> or \"expert blindness\". <a class=\"footnote-backref\" href=\"#fnref:expert-blindness\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:dk\">\n<p>This isn't Dunning-Kruger, because DK says that people think they are <em>better</em> than they actually are, and also <a href=\"https://www.mcgill.ca/oss/article/critical-thinking/dunning-kruger-effect-probably-not-real\" target=\"_blank\">may not actually be real</a>. <a class=\"footnote-backref\" href=\"#fnref:dk\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/the-jugglers-curse/", "published": "2025-01-22T18:50:40.000Z", "updated": "2025-01-22T18:50:40.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/", "title": "What are the Rosettas of formal specification?", "description": "<p>First of all, I just released version 0.6 of <em>Logic for Programmers</em>! You can get it <a href=\"https://leanpub.com/logic/\" target=\"_blank\">here</a>. Release notes in the footnote.<sup id=\"fnref:release-notes\"><a class=\"footnote-ref\" href=\"#fn:release-notes\">1</a></sup></p>\n<p>I've been thinking about my next project after the book's done. One idea is to do a survey of new formal specification languages. There's been a lot of new ones in the past few years (P, Quint, etc), plus some old ones I haven't critically examined (SPIN, mcrl2). I'm thinking of a brief overview of each, what's interesting about it, and some examples of the corresponding models.</p>\n<p>For this I'd want a set of \"Rosetta\" examples. <a href=\"https://rosettacode.org/wiki/Rosetta_Code\" target=\"_blank\">Rosetta Code</a> is a collection of programming tasks done in different languages. For example, <a href=\"https://rosettacode.org/wiki/99_bottles_of_beer\" target=\"_blank\">\"99 bottles of beer on the wall\"</a> in over 300 languages. If I wanted to make a Rosetta Code for specifications of concurrent systems, what examples would I use? </p>\n<h3>What makes a good Rosetta examples?</h3>\n<p>A good Rosetta example would be simple enough to understand and implement but also showcase the differences between the languages. </p>\n<p>A good example of a Rosetta example is <a href=\"https://github.com/hwayne/lets-prove-leftpad\" target=\"_blank\">leftpad for code verification</a>. Proving leftpad correct is short in whatever verification language you use. But the proofs themselves are different enough that you can compare what it's like to use code contracts vs with dependent types, etc. </p>\n<p>A <em>bad</em> Rosetta example is \"hello world\". While it's good for showing how to run a language, it doesn't clearly differentiate languages. Haskell's \"hello world\" is almost identical to BASIC's \"hello world\".</p>\n<p>Rosetta examples don't have to be flashy, but I <em>want</em> mine to be flashy. Formal specification is niche enough that regardless of my medium, most of my audience hasn't use it and may be skeptical. I always have to be selling. This biases me away from using things like dining philosophers or two-phase commit.</p>\n<p>So with that in mind, three ideas:</p>\n<h3>1. Wrapped Counter</h3>\n<p>A counter that starts at 1 and counts to N, after which it wraps around to 1 again.</p>\n<h4>Why it's good</h4>\n<p>This is a good introductory formal specification: it's a minimal possible stateful system without concurrency or nondeterminism. You can use it to talk about the basic structure of a spec, how a verifier works, etc. It also a good way of introducing \"boring\" semantics, like conditionals and arithmetic, and checking if the language does anything unusual with them. Alloy, for example, defaults to 4-bit signed integers, so you run into problems if you set N too high.<sup id=\"fnref:alloy\"><a class=\"footnote-ref\" href=\"#fn:alloy\">2</a></sup></p>\n<p>At the same time, wrapped counters are a common building block of complex systems. Lots of things can be represented this way: <code>N=1</code> is a flag or blinker, <code>N=3</code> is a traffic light, <code>N=24</code> is a clock, etc.</p>\n<p>The next example is better for showing basic <a href=\"https://www.hillelwayne.com/post/safety-and-liveness/\" target=\"_blank\">safety and liveness properties</a>, but this will do in a pinch. </p>\n<h3>2. Threads</h3>\n<p>A counter starts at 0. N threads each, simultaneously try to update the counter. They do this nonatomically: first they read the value of the counter and store that in a thread-local <code>tmp</code>, then they increment <code>tmp</code>, then they set the counter to <code>tmp</code>. The expected behavior is that the final value of the counter will be N.</p>\n<h4>Why it's good</h4>\n<p>The system as described is bugged. If two threads interleave the setlocal commands, one thread update can \"clobber\" the other and the counter can go backwards. To my surprise, most people <em>do not</em> see this error. So it's a good showcase of how the language actually finds real bugs, and how it can verify fixes.</p>\n<p>As to actual language topics: the spec covers concurrency and track process-local state. A good spec language should make it possible to adjust N without having to add any new variables. And it \"naturally\" introduces safety, liveness, and <a href=\"https://www.hillelwayne.com/post/action-properties/\" target=\"_blank\">action</a> properties.</p>\n<p>Finally, the thread spec is endlessly adaptable. I've used variations of it to teach refinement, resource starvation, fairness, livelocks, and hyperproperties. Tweak it a bit and you get dining philosophers.</p>\n<h3>3. Bounded buffer</h3>\n<p>We have a bounded buffer with maximum length <code>X</code>. We have <code>R</code> reader and <code>W</code> writer processes. Before writing, writers first check if the buffer is full. If full, the writer goes to sleep. Otherwise, the writer wakes up <em>a random</em> sleeping process, then pushes an arbitrary value. Readers work the same way, except they pop from the buffer (and go to sleep if the buffer is empty).</p>\n<p>The only way for a sleeping process to wake up is if another process successfully performs a read or write.</p>\n<h4>Why it's good</h4>\n<p>This shows process-local nondeterminism (in choosing which sleeping process to wake up), different behavior for different types of processes, and deadlocks: it's possible for every reader and writer to be asleep at the same time.</p>\n<p>The beautiful thing about this example: the spec can only deadlock if <code>X < 2*(R+W)</code>. This is the kind of bug you'd struggle to debug in real code. An in fact, people did struggle: even when presented with a minimal code sample and told there was a bug, many <a href=\"http://wiki.c2.com/?ExtremeProgrammingChallengeFourteen\" target=\"_blank\">testing experts couldn't find it</a>. Whereas a formal model of the same code <a href=\"https://www.hillelwayne.com/post/augmenting-agile/\" target=\"_blank\">finds the bug in seconds</a>. </p>\n<p>If a spec language can model the bounded buffer, then it's good enough for production systems.</p>\n<p>On top of that, the bug happens regardless of what writers actually put in the buffer, so you can abstract that all away. This example can demonstrate that you can leave implementation details out of a spec and still find critical errors.</p>\n<h2>Caveat</h2>\n<p>This is all with a <em>heavy</em> TLA+ bias. I've modeled all of these systems in TLA+ and it works pretty well for them. That is to say, none of these do things TLA+ is <em>bad</em> at: reachability, subtyping, transitive closures, unbound spaces, etc. I imagine that as I cover more specification languages I'll find new Rosettas.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:release-notes\">\n<ul>\n<li>Exercises are more compact, answers now show name of exercise in title</li>\n</ul>\n<ul>\n<li>\"Conditionals\" chapter has new section on nested conditionals</li>\n</ul>\n<ul>\n<li>\"Crash course\" chapter significantly rewritten</li>\n<li>Starting migrating to use consistently use <code>==</code> for equality and <code>=</code> for definition. Not everything is migrated yet</li>\n<li>\"Beyond Logic\" appendix does a <em>slightly</em> better job of covering HOL and constructive logic</li>\n<li>Addressed various reader feedback</li>\n<li>Two new exercises</li>\n</ul>\n<p><a class=\"footnote-backref\" href=\"#fnref:release-notes\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:alloy\">\n<p>You can change the int size in a model run, so this is more \"surprising footgun and inconvenience\" than \"fundamental limit of the specification language.\" Something still good to know! <a class=\"footnote-backref\" href=\"#fnref:alloy\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/what-are-the-rosettas-of-formal-specification/", "published": "2025-01-15T17:34:40.000Z", "updated": "2025-01-15T17:34:40.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/", "title": "\"Logic for Programmers\" Project Update", "description": "<p>Happy new year everyone!</p>\n<p>I released the first <em>Logic for Programmers</em> alpha six months ago. There's since been four new versions since then, with the November release putting us in beta. Between work and holidays I didn't make much progress in December, but there will be a 0.6 release in the next week or two.</p>\n<p>People have asked me if the book will ever be available in print, and my answer to that is \"when it's done\". To keep \"when it's done\" from being \"never\", I'm committing myself to <strong>have the book finished by July.</strong> That means roughly six more releases between now and the official First Edition. Then I will start looking for a way to get it printed.</p>\n<h3>The Current State and What Needs to be Done</h3>\n<p>Right now the book is 26,000 words. For the most part, the structure is set— I don't plan to reorganize the chapters much. But I still need to fix shortcomings identified by the reader feedback. In particular, a few topics need more on real world applications, and the Alloy chapter is pretty weak. There's also a bunch of notes and todos and \"fix this\"s I need to go over.</p>\n<p>I also need to rewrite the introduction and predicate logic chapters. Those haven't changed much since 0.1 and I need to go over them <em>very carefully</em>.</p>\n<p>After that comes copyediting.</p>\n<h4>Ugh, Copyediting</h4>\n<p>Copyediting means going through the entire book to make word and sentence sentence level changes to the flow. An example would be changing</p>\n<table>\n<thead>\n<tr>\n<th>From</th>\n<th>To</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>I said predicates are just “boolean functions”. That isn’t <em>quite</em> true.</td>\n<td>It's easy to think of predicates as just \"boolean\" functions, but there is a subtle and important difference.</td>\n</tr>\n</tbody>\n</table>\n<p>It's a tiny difference but it reads slightly better to me and makes the book slghtly better. Now repeat that for all 3000-odd sentences in the book and I'm done with copyediting!</p>\n<p>For the first pass, anyway. Copyediting is miserable. </p>\n<p>Some of the changes I need to make come from reader feedback, but most will come from going through it line-by-line with a copyeditor. Someone's kindly offered to do some of this for free, but I want to find a professional too. If you know anybody, let me know.</p>\n<h4>Formatting</h4>\n<p>The book, if I'm being honest, looks ugly. I'm using the default sphinx/latex combination for layout and typesetting. My thinking is it's not worth making the book pretty until it's worth reading. But I also want the book, when it's eventually printed, to look <em>nice</em>. At the very least it shouldn't have \"self-published\" vibes. </p>\n<p>I've found someone who's been giving me excellent advice on layout and I'm slowly mastering the LaTeX formatting arcana. It's gonna take a few iterations to get things right.</p>\n<h4>Front cover</h4>\n<p>Currently the front cover is this:</p>\n<p><img alt=\"Front cover\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/b42ee3de-9d8a-4729-809e-a8739741f0cf.png?w=960&fit=max\"/></p>\n<p>It works but gives \"programmer spent ten minutes in Inkscape\" vibes. I have a vision in my head for what would be nicer. A few people have recommended using Fiverr. So far the results haven't been that good, </p>\n<h4>Fixing Epub</h4>\n<p><em>Ugh</em></p>\n<p>I thought making an epub version would be kinder for phone reading, but it's such a painful format to develop for. Did you know that epub backlinks work totally different on kindle vs other ereaders? Did you know the only way to test if you got em working right is to load them up in a virtual kindle? The feedback loops are miserable. So I've been treating epub as a second-class citizen for now and only fixing the <em>worst</em> errors (like math not rendering properly), but that'll have to change as the book finalizes.</p>\n<h3>What comes next?</h3>\n<p>After 1.0, I get my book an ISBN and figure out how to make print copies. The margin on print is <em>way</em> lower than ebooks, especially if it's on-demand: the net royalties for <a href=\"https://kdp.amazon.com/en_US/help/topic/G201834330\" target=\"_blank\">Amazon direct publishing</a> would be 7 dollars on a 20-dollar book (as opposed to Leanpub's 16 dollars). Would having a print version double the sales? I hope so! Either way, a lot of people have been asking about print version so I want to make that possible.</p>\n<p>(I also want to figure out how to give people who already have the ebook a discount on print, but I don't know if that's feasible.)</p>\n<p>Then, I dunno, maybe make a talk or a workshop I can pitch to conferences. Once I have that I think I can call <em>LfP</em> complete... at least until the second edition.</p>\n<hr/>\n<p>Anyway none of that is actually technical so here's a quick fun thing. I spent a good chunk of my break reading the <a href=\"https://www.mcrl2.org/web/index.html\" target=\"_blank\">mCRL2 book</a>. mCRL2 defines an \"algebra\" for \"communicating processes\". As a very broad explanation, that's defining what it means to \"add\" and \"multiply\" two processes. What's interesting is that according to their definition, the algebra follows the distributive law, <em>but only if you multiply on the right</em>. eg</p>\n<div class=\"codehilite\"><pre><span></span><code>// VALID\n(a+b)*c = a*c + b*c\n\n// INVALID\na*(b+c) = a*b + a*c\n</code></pre></div>\n<p>This is the first time I've ever seen this in practice! Juries still out on the rest of the language.</p>\n<hr/>\n<h3>Videos and Stuff</h3>\n<ul>\n<li>My <em>DDD Europe</em> talk is now out! <a href=\"https://www.youtube.com/watch?v=uRmNSuYBUOU\" target=\"_blank\">What We Know We Don't Know</a> is about empirical software engineering in general, and software engineering research on Domain Driven Design in particular.</li>\n<li>I was interviewed in the last video on <a href=\"https://www.youtube.com/watch?v=yXxmSI9SlwM\" target=\"_blank\">Craft vs Cruft</a>'s \"Year of Formal Methods\". Check it out!</li>\n</ul>", "url": "https://buttondown.com/hillelwayne/archive/logic-for-programmers-project-update/", "published": "2025-01-07T18:49:40.000Z", "updated": "2025-01-07T18:49:40.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/", "title": "Formally modeling dreidel, the sequel", "description": "<p>Channukah's next week and that means my favorite pastime, complaining about how <a href=\"https://en.wikipedia.org/wiki/Dreidel#\" target=\"_blank\">Dreidel</a> is a bad game. Last year I formally modeled it in <a href=\"https://www.prismmodelchecker.org/\" target=\"_blank\">PRISM</a> to prove the game's not fun. But because I limited the model to only a small case, I couldn't prove the game was <em>truly</em> bad. </p>\n<p>It's time to finish the job.</p>\n<p><img alt=\"A flaming dreidel, from https://pixelsmerch.com/featured/flaming-dreidel-ilan-rosen.html\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/61233445-69a7-4fd4-a024-ee0dca0281c1.jpg?w=960&fit=max\"/></p>\n<h2>The Story so far</h2>\n<p>You can read the last year's newsletter <a href=\"https://buttondown.com/hillelwayne/archive/i-formally-modeled-dreidel-for-no-good-reason/\" target=\"_blank\">here</a> but here are the high-level notes.</p>\n<h3>The Game of Dreidel</h3>\n<ol>\n<li>Every player starts with N pieces (usually chocolate coins). This is usually 10-15 pieces per player.</li>\n<li>At the beginning of the game, and whenever the pot is empty, every play antes one coin into the pot.</li>\n<li>\n<p>Turns consist of spinning the dreidel. Outcomes are:</p>\n<ul>\n<li>נ (Nun): nothing happens.</li>\n<li>ה (He): player takes half the pot, rounded up.</li>\n<li>ג (Gimmel): player takes the whole pot, everybody antes.</li>\n<li>ש (Shin): player adds one of their coins to the pot.</li>\n</ul>\n</li>\n<li>\n<p>If a player ever has zero coins, they are eliminated. Play continues until only one player remains.</p>\n</li>\n</ol>\n<p>If you don't have a dreidel, you can instead use a four-sided die, but for the authentic experience you should wait eight seconds before looking at your roll.</p>\n<h3>PRISM</h3>\n<p><a href=\"https://www.prismmodelchecker.org/\" target=\"_blank\">PRISM</a> is a probabilistic modeling language, meaning you can encode a system with random chances of doing things and it can answer questions like \"on average, how many spins does it take before one player loses\" (64, for 4 players/10 coins) and \"what's the more likely to knock the first player out, shin or ante\" (ante is 2.4x more likely). You can see last year's model <a href=\"https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism\" target=\"_blank\">here</a>.</p>\n<p>The problem with PRISM is that it is absurdly inexpressive: it's a thin abstraction for writing giant <a href=\"https://en.wikipedia.org/wiki/Stochastic_matrix\" target=\"_blank\">stochastic matrices</a> and lacks basic affordances like lists or functions. I had to hardcode every possible roll for every player. This meant last year's model had two limits. First, it only handles four players, and I would have to write a new model for three or five players. Second, I made the game end as soon as one player <em>lost</em>:</p>\n<div class=\"codehilite\"><pre><span></span><code>formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0);\n</code></pre></div>\n<p>To fix both of these things, I thought I'd have to treat PRISM as a compilation target, writing a program that took a player count and output the corresponding model. But then December got super busy and I ran out of time to write a program. Instead, I stuck with four hardcoded players and extended the old model to run until victory.</p>\n<h2>The new model</h2>\n<p>These are all changes to <a href=\"https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-01-dreidel-prism\" target=\"_blank\">last year's model</a>.</p>\n<p>First, instead of running until one player is out of money, we run until three players are out of money.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"gd\">- formula done = (p1=0) | (p2=0) | (p3=0) | (p4=0);</span>\n<span class=\"gi\">+ formula done = </span>\n<span class=\"gi\">+ ((p1=0) & (p2=0) & (p3=0)) |</span>\n<span class=\"gi\">+ ((p1=0) & (p2=0) & (p4=0)) |</span>\n<span class=\"gi\">+ ((p1=0) & (p3=0) & (p4=0)) |</span>\n<span class=\"gi\">+ ((p2=0) & (p3=0) & (p4=0));</span>\n</code></pre></div>\n<p>Next, we change the ante formula. Instead of adding four coins to the pot and subtracting a coin from each player, we add one coin for each player left. <code>min(p1, 1)</code> is 1 if player 1 is still in the game, and 0 otherwise. </p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"gi\">+ formula ante_left = min(p1, 1) + min(p2, 1) + min(p3, 1) + min(p4, 1);</span>\n</code></pre></div>\n<p>We also have to make sure anteing doesn't end a player with negative money. </p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"gd\">- [ante] (pot = 0) & !done -> (pot'=pot+4) & (p1' = p1-1) & (p2' = p2-1) & (p3' = p3-1) & (p4' = p4-1);</span>\n<span class=\"gi\">+ [ante] (pot = 0) & !done -> (pot'=pot+ante_left) & (p1' = max(p1-1, 0)) & (p2' = max(p2-1, 0)) & (p3' = max(p3-1, 0)) & (p4' = max(p4-1, 0));</span>\n</code></pre></div>\n<p>Finally, we have to add logic for a player being \"out\". Instead of moving to the next player after each turn, we move to the next player still in the game. Also, if someone starts their turn without any coins (f.ex if they just anted their last coin), we just skip their turn. </p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"gi\">+ formula p1n = (p2 > 0 ? 2 : p3 > 0 ? 3 : 4);</span>\n\n<span class=\"gi\">+ [lost] ((pot != 0) & !done & (turn = 1) & (p1 = 0)) -> (turn' = p1n);</span>\n<span class=\"gd\">- [spin] ((pot != 0) & !done & (turn = 1)) -></span>\n<span class=\"gi\">+ [spin] ((pot != 0) & !done & (turn = 1) & (p1 != 0)) -></span>\n<span class=\"w\"> </span> 0.25: (p1' = p1-1) \n<span class=\"w\"> </span> & (pot' = min(pot+1, maxval)) \n<span class=\"gd\">- & (turn' = 2) //shin</span>\n<span class=\"gi\">+ & (turn' = p1n) //shin</span>\n</code></pre></div>\n<p>We make similar changes for all of the other players. You can see the final model <a href=\"https://gist.github.com/hwayne/f8724f0c83393c576b1e20ee4b76966d#file-02-dreidel-prism\" target=\"_blank\">here</a>.</p>\n<h3>Querying the model</h3>\n<div class=\"subscribe-form\"></div>\n<p>So now we have a full game of Dreidel that runs until the player ends. And now, <em>finally</em>, we can see the average number of spins a 4 player game will last.</p>\n<div class=\"codehilite\"><pre><span></span><code>./prism<span class=\"w\"> </span>dreidel.prism<span class=\"w\"> </span>-const<span class=\"w\"> </span><span class=\"nv\">M</span><span class=\"o\">=</span><span class=\"m\">10</span><span class=\"w\"> </span>-pf<span class=\"w\"> </span><span class=\"s1\">'R=? [F done]'</span><span class=\"w\"> </span>\n</code></pre></div>\n<p>In English: each player starts with ten coins. <code>R=?</code> means \"expected value of the 'reward'\", where 'reward' in this case means number of spins. <code>[F done]</code> weights the reward over all behaviors that reach (\"<strong>F</strong>inally\") the <code>done</code> state.</p>\n<div class=\"codehilite\"><pre><span></span><code>Result: 760.5607582661091\nTime for model checking: 384.17 seconds.\n</code></pre></div>\n<p>So there's the number: 760 spins.<sup id=\"fnref:ben\"><a class=\"footnote-ref\" href=\"#fn:ben\">1</a></sup> At 8 seconds a spin, that's almost two hours for <em>one</em> game.</p>\n<p>…Jesus, look at that runtime. Six minutes to test one query.</p>\n<p>PRISM has over a hundred settings that affect model checking, with descriptions like \"Pareto curve threshold\" and \"Use Backwards Pseudo SOR\". After looking through them all, I found this perfect combination of configurations that gets the runtime to a more manageable level: </p>\n<div class=\"codehilite\"><pre><span></span><code>./prism dreidel.prism \n<span class=\"w\"> </span> -const M=10 \n<span class=\"w\"> </span> -pf 'R=? [F done]' \n<span class=\"gi\">+ -heuristic speed</span>\n\nResult: 760.816255997373\nTime for model checking: 13.44 seconds.\n</code></pre></div>\n<p>Yes, that's a literal \"make it faster\" flag.</p>\n<p>Anyway, that's only the \"average\" number of spins, weighted across all games. Dreidel has a very long tail. To find that out, we'll use a variation on our query:</p>\n<div class=\"codehilite\"><pre><span></span><code>const C0; P=? [F <=C0 done]\n</code></pre></div>\n<p><code>P=?</code> is the <strong>P</strong>robability something happens. <code>F <=C0 done</code> means we <strong>F</strong>inally reach state <code>done</code> in at most <code>C0</code> steps. By passing in different values of <code>C0</code> we can get a sense of how long a game takes. Since \"steps\" includes passes and antes, this will overestimate the length of the game. But antes take time too and it should only \"pass\" on a player once per player, so this should still be a good metric for game length.</p>\n<div class=\"codehilite\"><pre><span></span><code>./prism dreidel.prism \n -const M=10 \n -const C0=1000:1000:5000\n -pf 'const C0; P=? [F <=C0 done]' \n -heuristic speed\n\nC0 Result\n1000 0.6259953274918795\n2000 0.9098575028069353\n3000 0.9783122218576754\n4000 0.994782069562932\n5000 0.9987446018004976\n</code></pre></div>\n<p>A full 10% of games don't finish in 2000 steps, and 2% pass the 3000 step barrier. At 8 seconds a roll/ante, 3000 steps is over <strong>six hours</strong>.</p>\n<p>Dreidel is a bad game.</p>\n<h3>More fun properties</h3>\n<p>As a sanity check, let's confirm last year's result, that it takes an average of 64ish spins before one player is out. In that model, we just needed to get the total reward. Now we instead want to get the reward until the first state where any of the players have zero coins. <sup id=\"fnref:co-safe\"><a class=\"footnote-ref\" href=\"#fn:co-safe\">2</a></sup></p>\n<div class=\"codehilite\"><pre><span></span><code>./prism dreidel.prism \n -const M=10 \n -pf 'R=? [F (p1=0 | p2=0 | p3=0 | p4=0)]' \n -heuristic speed\n\nResult: 63.71310116083396\nTime for model checking: 2.017 seconds.\n</code></pre></div>\n<p>Yep, looks good. With our new model we can also get the average point where two players are out and two players are left. PRISM's lack of abstraction makes expressing the condition directly a little painful, but we can cheat and look for the first state where <code>ante_left <= 2</code>.<sup id=\"fnref:ante_left\"><a class=\"footnote-ref\" href=\"#fn:ante_left\">3</a></sup></p>\n<div class=\"codehilite\"><pre><span></span><code>./prism dreidel.prism \n -const M=10 \n -pf 'R=? [F (ante_left <= 2)]' \n -heuristic speed\n\nResult: 181.92839196680023\n</code></pre></div>\n<p>It takes twice as long to eliminate the second player as it takes to eliminate the first, and the remaining two players have to go for another 600 spins.</p>\n<p>Dreidel is a bad game.</p>\n<h2>The future</h2>\n<p>There's two things I want to do next with this model. The first is script up something that can generate the PRISM model for me, so I can easily adjust the number of players to 3 or 5. The second is that PRISM has a <a href=\"https://www.prismmodelchecker.org/manual/PropertySpecification/Filters\" target=\"_blank\">filter-query</a> feature I don't understand but I <em>think</em> it could be used for things like \"if a player gets 75% of the pot, what's the probability they lose anyway\". Otherwise you have to write wonky queries like <code>(P =? [F p1 = 30 & (F p1 = 0)]) / (P =? [F p1 = 0])</code>.<sup id=\"fnref:lose\"><a class=\"footnote-ref\" href=\"#fn:lose\">4</a></sup> But I'm out of time again, so this saga will have to conclude next year.</p>\n<p>I'm also faced with the terrible revelation that I might be the biggest non-academic user of PRISM.</p>\n<hr/>\n<h4><em>Logic for Programmers</em> Khanukah Sale</h4>\n<p>Still going on! You can get <em>LFP</em> for <a href=\"https://leanpub.com/logic/c/hannukah-presents\" target=\"_blank\">40% off here</a> from now until the end of Xannukkah (Jan 2).<sup id=\"fnref:joke\"><a class=\"footnote-ref\" href=\"#fn:joke\">5</a></sup></p>\n<h4>I'm in the Raku Advent Calendar!</h4>\n<p>My piece is called <a href=\"https://raku-advent.blog/2024/12/11/day-11-counting-up-concurrency/\" target=\"_blank\">counting up concurrencies</a>. It's about using Raku to do some combinatorics! Read the rest of the blog too, it's great</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:ben\">\n<p>This is different from the <a href=\"https://www.slate.com/articles/life/holidays/2014/12/rules_of_dreidel_the_hannukah_game_is_way_too_slow_let_s_speed_it_up.html\" target=\"_blank\">original anti-Dreidel article</a>: Ben got <em>860</em> spins. That's the average spins if you round <em>down</em> on He, not up. Rounding up on He leads to a shorter game because it means He can empty the pot, which means more antes, and antes are what knocks most players out. <a class=\"footnote-backref\" href=\"#fnref:ben\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:co-safe\">\n<p>PRISM calls this <a href=\"https://www.prismmodelchecker.org/manual/PropertySpecification/Reward-basedProperties\" target=\"_blank\">\"co-safe LTL reward\"</a> and does <em>not</em> explain what that means, nor do most of the papers I found referencing \"co-safe LTL\". <a href=\"https://mengguo.github.io/personal_site/papers/pdf/guo2016task.pdf\" target=\"_blank\">Eventually</a> I found one that defined it as \"any property that only uses X, U, F\". <a class=\"footnote-backref\" href=\"#fnref:co-safe\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:ante_left\">\n<p>Here's the exact point where I realize I could have defined <code>done</code> as <code>ante_left = 1</code>. Also checking for <code>F (ante_left = 2)</code> gives an expected number of spins as \"infinity\". I have no idea why. <a class=\"footnote-backref\" href=\"#fnref:ante_left\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n<li id=\"fn:lose\">\n<p>10% chances at 4 players / 10 coins. And it takes a minute even <em>with</em> fast mode enabled. <a class=\"footnote-backref\" href=\"#fnref:lose\" title=\"Jump back to footnote 4 in the text\">↩</a></p>\n</li>\n<li id=\"fn:joke\">\n<p>This joke was funnier before I made the whole newsletter about Chanukahh. <a class=\"footnote-backref\" href=\"#fnref:joke\" title=\"Jump back to footnote 5 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/formally-modeling-dreidel-the-sequel/", "published": "2024-12-18T16:58:59.000Z", "updated": "2024-12-18T16:58:59.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/stroustrups-rule/", "title": "Stroustrup's Rule", "description": "<p>Just finished two weeks of workshops and am <em>exhausted</em>, so this one will be light. </p>\n<h3>Hanuka Sale</h3>\n<p><em>Logic for Programmers</em> is on sale until the end of Chanukah! That's Jan 2nd if you're not Jewish. <a href=\"https://leanpub.com/logic/c/hannukah-presents\" target=\"_blank\">Get it for 40% off here</a>.</p>\n<h1>Stroustrup's Rule</h1>\n<p>I first encountered <strong>Stroustrup's Rule</strong> on this <a href=\"https://web.archive.org/web/20240914141601/https:/www.thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/\" target=\"_blank\">defunct webpage</a>:</p>\n<blockquote>\n<p>One of my favorite insights about syntax design appeared in a <a href=\"https://learn.microsoft.com/en-us/shows/lang-next-2014/keynote\" target=\"_blank\">retrospective on C++</a><sup id=\"fnref:timing\"><a class=\"footnote-ref\" href=\"#fn:timing\">1</a></sup> by Bjarne Stroustrup:</p>\n<ul>\n<li>For new features, people insist on <strong>LOUD</strong> explicit syntax. </li>\n<li>For established features, people want terse notation.</li>\n</ul>\n</blockquote>\n<p>The blogger gives the example of option types in Rust. Originally, the idea of using option types to store errors was new for programmers, so the syntax for passing an error was very explicit:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"n\">file</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"k\">match</span><span class=\"w\"> </span><span class=\"n\">File</span><span class=\"p\">::</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"s\">\"file.txt\"</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"nb\">Ok</span><span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">=></span><span class=\"w\"> </span><span class=\"n\">file</span><span class=\"p\">,</span>\n<span class=\"w\"> </span><span class=\"nb\">Err</span><span class=\"p\">(</span><span class=\"n\">err</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">=></span><span class=\"w\"> </span><span class=\"p\">{</span><span class=\"w\"> </span><span class=\"k\">return</span><span class=\"w\"> </span><span class=\"n\">err</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p>Once people were more familiar with it, Rust added the <code>try!</code> macro to reduce boilerplate, and finally the <a href=\"https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md\" target=\"_blank\"><code>?</code> operator</a> to streamline error handling further.</p>\n<p>I see this as a special case of <a href=\"http://teachtogether.tech/en/index.html#s:models\" target=\"_blank\">mental model development</a>: when a feature is new to you, you don't have an internal mental model so need all of the explicit information you can get. Once you're familiar with it, explicit syntax is visual clutter and hinders how quickly you can parse out information.</p>\n<p>(One example I like: which is more explicit, <code>user_id</code> or <code>user_identifier</code>? Which do experienced programmers prefer?)</p>\n<p>What's interesting is that it's often the <em>same people</em> on both sides of the spectrum. Beginners need explicit syntax, and as they become experts, they prefer terse syntax. </p>\n<p>The rule applies to the overall community, too. At the beginning of a language's life, everybody's a beginner. Over time the ratio of experts to beginners changes, and this leads to more focus on \"expert-friendly\" features, like terser syntax.</p>\n<p>This can make it harder for beginners to learn the language. There was a lot of drama in Python over the <a href=\"https://peps.python.org/pep-0572/\" target=\"_blank\">\"walrus\" assignment operator</a>:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\"># Without walrus</span>\n<span class=\"n\">val</span> <span class=\"o\">=</span> <span class=\"nb\">dict</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">key</span><span class=\"p\">)</span> <span class=\"c1\"># `None` if key absent</span>\n<span class=\"k\">if</span> <span class=\"n\">val</span><span class=\"p\">:</span>\n <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"n\">val</span><span class=\"p\">)</span>\n\n\n<span class=\"c1\"># With walrus</span>\n<span class=\"k\">if</span> <span class=\"n\">val</span> <span class=\"o\">:=</span> <span class=\"nb\">dict</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">key</span><span class=\"p\">):</span>\n <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"n\">val</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Experts supported it because it made code more elegant, teachers and beginners opposed it because it made the language harder to learn. Explicit syntax vs terse notation.</p>\n<p>Does this lead to languages bloating over time?</p>\n<h3>In Teaching</h3>\n<p>I find that when I teach language workshops I have to actively work against Stroustrup's Rule. The terse notation that easiest for <em>me</em> to read is bad for beginners, who need the explicit syntax that I find grating.</p>\n<p>One good example is type invariants in TLA+. Say you have a set of workers, and each worker has a counter. Here's two ways to say that every worker's counter is a non-negative integer:</p>\n<div class=\"codehilite\"><pre><span></span><code>\\* Bad\n\\A w \\in Workers: counter[w] >= 0\n\n\\* Good\ncounter \\in [Workers -> Nat]\n</code></pre></div>\n<p>The first way literally tests that for every worker, <code>counter[w]</code> is non-negative. The second way tests that the <code>counter</code> mapping as a whole is an element of the appropriate \"function set\"— all functions between workers and natural numbers.</p>\n<p>The function set approach is terser, more elegant, and preferred by TLA+ experts. But I teach the \"bad\" way because it makes more sense to beginners.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:timing\">\n<p>Starts minute 23. <a class=\"footnote-backref\" href=\"#fnref:timing\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/stroustrups-rule/", "published": "2024-12-11T17:32:53.000Z", "updated": "2024-12-11T17:32:53.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/hyperproperties/", "title": "Hyperproperties", "description": "<p>I wrote about <a href=\"https://hillelwayne.com/post/hyperproperties/\" target=\"_blank\">hyperproperties on my blog</a> four years ago, but now an intriguing client problem got me thinking about them again.<sup id=\"fnref:client\"><a class=\"footnote-ref\" href=\"#fn:client\">1</a></sup></p>\n<p>We're using TLA+ to model a system that starts in state A, and under certain complicated conditions <code>P</code>, transitions to state B. They also had a flag <code>f</code> that, when set, used a different complicated condition <code>Q</code> to check the transitions. As a quick <a href=\"https://www.hillelwayne.com/post/decision-tables/\" target=\"_blank\">decision table</a> (from state <code>A</code>):</p>\n<table>\n<thead>\n<tr>\n<th>f</th>\n<th>P</th>\n<th>Q</th>\n<th>state'</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>F</td>\n<td>F</td>\n<td>-</td>\n<td>A</td>\n</tr>\n<tr>\n<td>F</td>\n<td>T</td>\n<td>-</td>\n<td>B</td>\n</tr>\n<tr>\n<td>T</td>\n<td>F</td>\n<td>F</td>\n<td>A</td>\n</tr>\n<tr>\n<td>T</td>\n<td>F</td>\n<td>T</td>\n<td>B</td>\n</tr>\n<tr>\n<td>T</td>\n<td>T</td>\n<td>F</td>\n<td><strong>impossible</strong></td>\n</tr>\n<tr>\n<td>T</td>\n<td>T</td>\n<td>T</td>\n<td>B</td>\n</tr>\n</tbody>\n</table>\n<p>The interesting bit is the second-to-last row: Q has to be <em>strictly</em> more permissible than P. The client wanted to verify the property that \"the system more aggressively transitions when <code>f</code> is set\", ie there is no case where the machine transitions <em>only if <code>f</code> is false</em>.</p>\n<p><a href=\"https://www.hillelwayne.com/post/safety-and-liveness/\" target=\"_blank\">Regular system properties</a> are specified over states in a single sequence of states (behaviors). <strong>Hyperproperties</strong> can hold over <em>sets</em> of sequences of states. Here the hyperproperties are:</p>\n<blockquote>\n<ol>\n<li>For any two states X and Y in separate behaviors, if the only difference in variable-state between X and Y is that <code>X.f = TRUE</code>, then whenever Y transitions to B, so does X.</li>\n<li>There is at least one such case where X transitions and Y does not.</li>\n</ol>\n</blockquote>\n<p>That's pretty convoluted, which is par for the course with hyperproperties! It makes a little more sense if you have all of the domain knowledge and specifics. </p>\n<p>The key thing is that makes this a hyperproperty is that you can't <em>just</em> look at individual behaviors to verify it. Imagine if, when <code>f</code> is true, we <em>never</em> transition to state B. Is that a violation of (1)? Not if we never transition when <code>f</code> is false either! To prove a violation, you need to find a behavior where <code>f</code> is false <em>and</em> the state is otherwise the same <em>and</em> we transition to B anyway.</p>\n<h4>Aside: states in states in states</h4>\n<p>I dislike how \"state\" refers to three things:</p>\n<ol>\n<li>The high-level \"transition state\" of a state-machine</li>\n<li>A single point in time of a system (the \"state space\")</li>\n<li>The mutable data inside your system's <a href=\"https://www.hillelwayne.com/post/world-vs-machine/\" target=\"_blank\">machine</a>.</li>\n</ol>\n<p>These are all \"close\" to each other but <em>just</em> different enough to make conversations confusing. Software is pretty bad about reusing colloquial words like this; don't even get me <em>started</em> on the word \"design\".</p>\n<h3>There's a reason we don't talk about hyperproperties</h3>\n<p>Or three reasons. First of all, hyperproperties make up a <em>vanishingly small</em> percentage of the stuff in a system we care about. We only got to \"<code>f</code> makes the system more aggressive\" after checking at least a dozen other simpler and <em>more important</em> not-hyper properties.</p>\n<p>Second, <em>most</em> formal specification languages can't express hyperproperties, and the ones that can are all academic research projects. Modeling systems is hard enough without a generalized behavior notation!</p>\n<p>Third, hyperproperties are astoundingly expensively to check. As an informal estimation, for a state space of size <code>N</code> regular properties are checked across <code>N</code> individual states and 2-behavior hyperproperties (2-props) are checked across <code>N²</code> pairs. So for a small state space of just a million states, the 2-prop needs to be checked across a <em>trillion</em> pairs. </p>\n<p>These problems don't apply to \"hyperproperties\" of functions, just systems. Functions have a lot of interesting hyperproperties, there's an easy way to represent them (call the function twice in a test), and quadratic scaling isn't so bad if you're only testing 100 inputs or so. That's why so-called <a href=\"https://www.hillelwayne.com/post/metamorphic-testing/\" target=\"_blank\">metamorphic testing</a> of functions can be useful.</p>\n<h3>Checking Hyperproperties Anyway</h3>\n<p>If we <em>do</em> need to check a hyperproperty, there's a few ways we can approach it. </p>\n<p>The easiest way is to cheat and find a regular prop that implies the hyperproperty. In client's case, we can abstract <code>P</code> and <code>Q</code> into pure functions and then test that there's no input where <code>P</code> is true and <code>Q</code> is false. In TLA+, this would look something like</p>\n<div class=\"codehilite\"><pre><span></span><code>\\* TLA+\nQLooserThanP ==\n \\A i1 \\in InputSet1, i2 \\in Set2: \\* ...\n P(i1, i2, …) => Q(i1, i2, …)\n</code></pre></div>\n<p>Of course we can't always encapsulate this way, and this can't catch bugs like \"we accidentally use <code>P</code> even if <code>f</code> is true\". But it gets the job done.</p>\n<p>Another way is something I talked about in the <a href=\"https://hillelwayne.com/post/hyperproperties/\" target=\"_blank\">original hyperproperty post</a>: lifting specs into hyperspecs. We create a new spec that initializes two copies of our main spec, runs them in parallel, and then compares their behaviors. See the post for an example. Writing a hyperspec keeps us entirely in TLA+ but takes a lot of work and is <em>very</em> expensive to check. Depending on the property we want to check, we can sometimes find simple optimizations.</p>\n<p>The last way is something <a href=\"https://hillelwayne.com/post/graphing-tla/\" target=\"_blank\">I explored last year</a>: dump the state graph to disk and treat the hyperproperty as a graph property. In this case, the graph property would be something like </p>\n<blockquote>\n<p>Find all graph edges representing an A → B transition. Take all the source nodes of each where <code>f = false</code>. For each such source node, find the corresponding node that's identical except for <code>f = true</code>. That node should be the source of an A → B edge.</p>\n</blockquote>\n<p>Upside is you don't have to make any changes to the original spec. Downside is you have to use another programming language for analysis. Also, <a href=\"https://hillelwayne.com/post/graph-types/\" target=\"_blank\">analyzing graphs is terrible</a>. But I think this overall the most robust approach to handling hyperproperties, to be used when \"cheating\" fails.</p>\n<hr/>\n<p>What fascinates me most about this is the four-year gap between \"I learned and wrote about hyperproperties\" and \"I have to deal with hyperproperties in my job.\" This is one reason learning for the sake of learning can have a lot of long-term benefits.</p>\n<hr/>\n<h3>Blog Rec</h3>\n<p>This week's rec is <a href=\"https://robertheaton.com/\" target=\"_blank\">Robert Heaton</a>. It's a \"general interest\" software engineering blog with a focus on math, algorithms, and security. Some of my favorites:</p>\n<ul>\n<li><a href=\"https://robertheaton.com/preventing-impossible-game-levels-using-cryptography/\" target=\"_blank\">Preventing impossible game levels using cryptography</a> and the whole \"Steve Steveington\" series</li>\n<li><a href=\"https://robertheaton.com/2019/06/24/i-was-7-words-away-from-being-spear-phished/\" target=\"_blank\">I was 7 words away from being spear-phished</a> is a great deep dive into one targeted scam</li>\n<li><a href=\"https://robertheaton.com/2019/02/24/making-peace-with-simpsons-paradox/\" target=\"_blank\">Making peace with Simpson's Paradox</a> is the best explanation of Simpson's Paradox I've ever read.</li>\n</ul>\n<p>Other good ones are <a href=\"https://robertheaton.com/pyskywifi/\" target=\"_blank\">PySkyWiFi: completely free, unbelievably stupid wi-fi on long-haul flights</a> and <a href=\"https://robertheaton.com/interview/\" target=\"_blank\">How to pass a coding interview with me</a>. The guy's got <em>breadth</em>.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:client\">\n<p>I do formal methods consulting btw. <a href=\"https://www.hillelwayne.com/consulting/\" target=\"_blank\">Hire me!</a> <a class=\"footnote-backref\" href=\"#fnref:client\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/hyperproperties/", "published": "2024-11-19T19:34:54.000Z", "updated": "2024-11-19T19:34:54.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/", "title": "Five Unusual Raku Features", "description": "<h3><a href=\"https://leanpub.com/logic/\" target=\"_blank\"><em>Logic for Programmers</em></a> is now in Beta!</h3>\n<p><a href=\"https://leanpub.com/logic/\" target=\"_blank\">v0.5 marks the official end of alpha</a>! With the new version, all of the content I wanted to put in the book is now present, and all that's left is copyediting, proofreading, and formatting. Which will probably take as long as it took to actually write the book. You can see the release notes in the footnote.<sup id=\"fnref:release-notes\"><a class=\"footnote-ref\" href=\"#fn:release-notes\">1</a></sup></p>\n<p>And I've got a snazzy new cover:</p>\n<p><img alt=\"The logic for programmers cover, a 40x zoom of a bird feather\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/26c75f1e-e60a-4328-96e5-9878d96d3e53.png?w=960&fit=max\"/></p>\n<p>(I don't actually like the cover that much but it <em>looks</em> official enough until I can pay an actual cover designer.)</p>\n<h1>\"Five\" Unusual Raku Features</h1>\n<p>Last year I started learning Raku, and the sheer bizarreness of the language left me describing it as <a href=\"https://buttondown.com/hillelwayne/archive/raku-a-language-for-gremlins/\" target=\"_blank\">a language for gremlins</a>. Now that I've used it in anger for over a year, I have a better way of describing it:</p>\n<blockquote>\n<p>Raku is a laboratory for language features.</p>\n</blockquote>\n<p>This is why it has <a href=\"https://docs.raku.org/language/concurrency\" target=\"_blank\">five different models of concurrency</a> and eighteen ways of doing anything else, because the point is to <em>see</em> what happens. It also explains why many of the features interact so strangely and why there's all that odd edge-case behavior. Getting 100 experiments polished and playing nicely with each other is much harder than running 100 experiments; we can sort out the polish <em>after</em> we figure out which ideas are good ones.</p>\n<p>So here are \"five\" Raku experiments you could imagine seeing in another programming language. If you squint.</p>\n<h3><a href=\"https://docs.raku.org/type/Junction\" target=\"_blank\">Junctions</a></h3>\n<p>Junctions are \"superpositions of possible values\". Applying an operation to a junction instead applies it to every value inside the junction. </p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"mi\">2</span><span class=\"o\">|</span><span class=\"mi\">10</span>\n<span class=\"nb\">any</span>(<span class=\"mi\">2</span>, <span class=\"mi\">10</span>)\n\n> <span class=\"mi\">2</span><span class=\"nv\">&10</span> + <span class=\"mi\">3</span>\n<span class=\"nb\">all</span>(<span class=\"mi\">5</span>, <span class=\"mi\">13</span>)\n\n>(<span class=\"mi\">1</span><span class=\"nv\">&2</span>) + (<span class=\"mi\">10</span><span class=\"o\">^</span><span class=\"mi\">20</span>)\n<span class=\"nb\">all</span>(<span class=\"nb\">one</span>(<span class=\"mi\">11</span>, <span class=\"mi\">21</span>), <span class=\"nb\">one</span>(<span class=\"mi\">12</span>, <span class=\"mi\">22</span>))\n</code></pre></div>\n<p>As you can probably tell from the <code>all</code>s and <code>any</code>s, junctions are a feature meant for representing boolean formula. There's no way to destructure a junction, and the only way to use it is to collapse it to a boolean first.</p>\n<div class=\"codehilite\"><pre><span></span><code>> (<span class=\"mi\">1</span><span class=\"nv\">&2</span>) + (<span class=\"mi\">10</span><span class=\"o\">^</span><span class=\"mi\">20</span>) < <span class=\"mi\">15</span>\n<span class=\"nb\">all</span>(<span class=\"nb\">one</span>(<span class=\"nb\">True</span>, <span class=\"nb\">False</span>), <span class=\"nb\">one</span>(<span class=\"nb\">True</span>, <span class=\"nb\">False</span>))\n\n<span class=\"c1\"># so coerces junctions to booleans</span>\n> <span class=\"nb\">so</span> (<span class=\"mi\">1</span><span class=\"nv\">&2</span>) + (<span class=\"mi\">10</span><span class=\"o\">^</span><span class=\"mi\">20</span>) < <span class=\"mi\">15</span>\n<span class=\"nb\">True</span>\n\n> <span class=\"nb\">so</span> (<span class=\"mi\">1</span><span class=\"nv\">&2</span>) + (<span class=\"mi\">10</span><span class=\"o\">^</span><span class=\"mi\">20</span>) > <span class=\"mi\">0</span>\n<span class=\"nb\">False</span>\n\n> <span class=\"mi\">16</span> %% (<span class=\"mi\">3</span><span class=\"nv\">&5</span>) ?? <span class=\"s\">\"fizzbuzz\"</span> !! *\n*\n</code></pre></div>\n<p>The real interesting thing for me is how Raku elegantly uses junctions to represent quantifiers. In most languages, you either have the function <code>all(list[T], T -> bool)</code> or the method <code>[T].all(T -> bool)</code>, both of which apply the test to every element of the list. In Raku, though, <code>list.all</code> doesn't take <em>anything</em>, it's just a niladic method that turns the list into a junction. </p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"k\">my</span> <span class=\"nv\">$x</span> = <span class=\"s\"><1 2 3></span>.<span class=\"nb\">all</span>\n<span class=\"nb\">all</span>(<span class=\"mi\">1</span>, <span class=\"mi\">2</span>, <span class=\"mi\">3</span>)\n> <span class=\"nb\">is-prime</span>(<span class=\"nv\">$x</span>)\n<span class=\"nb\">all</span>(<span class=\"nb\">False</span>, <span class=\"nb\">True</span>, <span class=\"nb\">True</span>)\n</code></pre></div>\n<p>This means we can combine junctions. If Raku didn't already have a <code>unique</code> method, we could build it by saying \"are all elements equal to exactly one element?\"</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"nb\">so</span> {.<span class=\"nb\">all</span> == .<span class=\"nb\">one</span>}(<span class=\"s\"><1 2 3 7></span>)\n<span class=\"nb\">True</span>\n\n> <span class=\"nb\">so</span> {.<span class=\"nb\">all</span> == .<span class=\"nb\">one</span>}(<span class=\"s\"><1 2 3 7 2></span>)\n<span class=\"nb\">False</span>\n</code></pre></div>\n<h3><a href=\"https://docs.raku.org/type/Whatever\" target=\"_blank\">Whatevers</a></h3>\n<p><code>*</code> is the \"whatever\" symbol and has a lot of different roles in Raku.<sup id=\"fnref:analogs\"><a class=\"footnote-ref\" href=\"#fn:analogs\">2</a></sup> Some functions and operators have special behavior when passed a <code>*</code>. In a range or sequence, <code>*</code> means \"unbound\".</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"mi\">1</span>..*\n<span class=\"mi\">1</span><span class=\"o\">..</span><span class=\"n\">Inf</span>\n\n> (<span class=\"mi\">2</span>,<span class=\"mi\">4</span>,<span class=\"mi\">8</span>...*)[<span class=\"mi\">17</span>]\n<span class=\"mi\">262144</span>\n</code></pre></div>\n<p>The main built-in use, though, is that expressions with <code>*</code> are lifted into anonymous functions. This is called \"whatever-priming\" and produces a <code>WhateverCode</code>, which is indistinguishable from other functions except for the type.</p>\n<div class=\"codehilite\"><pre><span></span><code>> {<span class=\"nv\">$_</span> + <span class=\"mi\">10</span>}(<span class=\"mi\">2</span>)\n<span class=\"mi\">12</span>\n\n> (* + <span class=\"mi\">10</span>)(<span class=\"mi\">2</span>)\n<span class=\"mi\">12</span>\n\n> (^<span class=\"mi\">10</span>).<span class=\"n\">map</span>(* % <span class=\"mi\">2</span>)\n(<span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">0</span> <span class=\"mi\">1</span>)\n</code></pre></div>\n<p>There's actually a bit of weird behavior here: if <em>two</em> whatevers appear in the expression, they become separate positional variables. <code>(2, 30, 4, 50).map(* + *)</code> returns <code>(32, 54)</code>. This makes it easy to express <a href=\"https://docs.raku.org/language/operators#infix_...\" target=\"_blank\">a tricky Fibonacci definition</a> but otherwise I don't see how it's better than making each <code>*</code> the same value.</p>\n<p>Regardless, priming is useful because <em>so many</em> Raku methods are overloaded to take functions. You get the last element of a list with <code>l[*-1]</code>. This <em>looks</em> like standard negative-index syntax, but what actually happens is that when <code>[]</code> is passed a function, it passes in list length and looks up the result. So if the list has 10 elements, <code>l[*-1] = l[10-1] = l[9]</code>, aka the last element. Similarly, <code>l.head(2)</code> is the first two elements of a list, <code>l.head(*-2)</code> is all-but-the-last-two.</p>\n<p>We can pass other functions to <code>[]</code>, which e.g. makes implementing ring buffers easy.</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"k\">my</span> <span class=\"nv\">@x</span> = ^<span class=\"mi\">10</span>\n[<span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">2</span> <span class=\"mi\">3</span> <span class=\"mi\">4</span> <span class=\"mi\">5</span> <span class=\"mi\">6</span> <span class=\"mi\">7</span> <span class=\"mi\">8</span> <span class=\"mi\">9</span>]\n\n> <span class=\"nv\">@x</span>[<span class=\"mi\">95</span> % *]--; <span class=\"nv\">@x</span>\n[<span class=\"mi\">0</span> <span class=\"mi\">1</span> <span class=\"mi\">2</span> <span class=\"mi\">3</span> <span class=\"mi\">4</span> <span class=\"mi\">4</span> <span class=\"mi\">6</span> <span class=\"mi\">7</span> <span class=\"mi\">8</span> <span class=\"mi\">9</span>]\n</code></pre></div>\n<h3><a href=\"https://docs.raku.org/language/regexes\" target=\"_blank\">Regular Expressions</a></h3>\n<p>There are two basic standards for regexes: POSIX regexes and Perl-compatible regexes (PCRE). POSIX regexes are a terrible mess of backslashes and punctuation. PCRE is backwards compatible with POSIX and is a more terrible mess of backslashes and punctuation. Most languages follow the PCRE standard, but Perl 6 breaks backwards compatibility with an entirely new regex syntax. </p>\n<p>The most obvious improvement: <a href=\"https://docs.raku.org/language/regexes#Subrules\" target=\"_blank\">composability</a>. In most languages \"combine\" two regexes by concating their strings together, which is terrible for many, many reasons. Raku has the standard \"embed another regex\" syntax: <code>/< foo >+/</code> matches one-or-more of the <code>foo</code> regex without <code>foo</code> \"leaking\" into the top regex. </p>\n<p>This already does a lot to make regexes more tractable: you can break a complicated regular expression down into simpler and more legible parts. And in fact this is how Raku supports <a href=\"https://docs.raku.org/language/grammars\" target=\"_blank\">parsing grammars</a> as a builtin language feature. I've only used grammars once but it <a href=\"https://www.hillelwayne.com/post/picat/\" target=\"_blank\">was quite helpful</a>.</p>\n<p>Since we're breaking backwards compatibility anyway, we can now add lots of small QOLs. There's a <a href=\"https://docs.raku.org/language/regexes#Modified_quantifier:_%,_%%\" target=\"_blank\">value separator</a> modifier: <code>\\d+ % ','</code> matches <code>1</code> / <code>1,2</code> / <code>1,1,4</code> but not <code>1,</code> or <code>12</code>. <a href=\"https://docs.raku.org/language/regexes#Lookaround_assertions\" target=\"_blank\">Lookaheads</a> and non-capturing groups aren't nonsense glyphs. <code>r1 && r2</code> only matches strings that match <em>both</em> <code>r1</code> and <code>r2</code>. Backtracking can be stopped with <a href=\"https://docs.raku.org/language/regexes#Preventing_backtracking:_:\" target=\"_blank\">:</a>. Whitespace is ignored by default and has to be explicitly enabled in match patterns.</p>\n<p>There's more stuff Raku does with actually <em>processing</em> regular expressions, but the regex notation is something that might actually appear in another language someday. </p>\n<p style=\"height:16px; margin:0px !important;\"></p>\n<h3><a href=\"https://docs.raku.org/language/operators#Hyper_operators\" target=\"_blank\">Hyperoperators</a></h3>\n<p>This is a small one compared to the other features, but it's also the thing I miss most often in other languages. The most basic form <code>l>>.method</code> is basically equivalent to <code>map</code>, except it also recursively descends into sublists.</p>\n<div class=\"codehilite\"><pre><span></span><code>> [<span class=\"mi\">1</span>, [<span class=\"mi\">2</span>, <span class=\"mi\">3</span>], <span class=\"mi\">4</span>]>>.<span class=\"nb\">succ</span>\n[<span class=\"mi\">2</span> [<span class=\"mi\">3</span> <span class=\"mi\">4</span>] <span class=\"mi\">5</span>]\n</code></pre></div>\n<p>This is more useful than it looks because any function call <code>f(list, *args)</code> can be rewritten in \"method form\" <code>list.&f(*args)</code>, so <code>>>.</code> becomes the generalized mapping operator. You can use it with whatevers, too.</p>\n<div class=\"codehilite\"><pre><span></span><code>> [<span class=\"mi\">1</span>, [<span class=\"mi\">2</span>, <span class=\"mi\">3</span>], <span class=\"mi\">4</span>]>>.&(*+<span class=\"mi\">1</span>)\n[<span class=\"mi\">2</span> [<span class=\"mi\">3</span> <span class=\"mi\">4</span>] <span class=\"mi\">5</span>]\n</code></pre></div>\n<p>Anyway, the more generalized <em>binary</em> hyperoperator <code>l1 << op >> l2</code><sup id=\"fnref:spaces\"><a class=\"footnote-ref\" href=\"#fn:spaces\">3</a></sup> applies <code>op</code> elementwise to the two lists, looping the shorter list until the longer list is exhausted. <code>>>op>></code> / <code><< op<<</code> are the same except they instead loop until the lhs/rhs list is exhausted. Whew!</p>\n<div class=\"codehilite\"><pre><span></span><code>> [<span class=\"mi\">1</span>, <span class=\"mi\">2</span>, <span class=\"mi\">3</span>, <span class=\"mi\">4</span>, <span class=\"mi\">5</span>] <span class=\"s\"><<+></span>> [<span class=\"mi\">10</span>, <span class=\"mi\">20</span>]\n[<span class=\"mi\">11</span> <span class=\"mi\">22</span> <span class=\"mi\">13</span> <span class=\"mi\">24</span> <span class=\"mi\">15</span>]\n\n> [<span class=\"mi\">1</span>, <span class=\"mi\">2</span>, <span class=\"mi\">3</span>, <span class=\"mi\">4</span>, <span class=\"mi\">5</span>] <span class=\"s\"><<+<< [10, 20]</span>\n<span class=\"s\">[11 22]</span>\n\n<span class=\"s\">> [1, 2, 3, 4, 5] >></span>+>> [<span class=\"mi\">10</span>, <span class=\"mi\">20</span>]\n[<span class=\"mi\">11</span> <span class=\"mi\">22</span> <span class=\"mi\">13</span> <span class=\"mi\">24</span> <span class=\"mi\">15</span>]\n\n<span class=\"c1\"># Also works with single values</span>\n> [<span class=\"mi\">1</span>, <span class=\"mi\">2</span>, <span class=\"mi\">3</span>, <span class=\"mi\">4</span>, <span class=\"mi\">5</span>] <span class=\"s\"><<+></span>> <span class=\"mi\">10</span>\n[<span class=\"mi\">11</span> <span class=\"mi\">12</span> <span class=\"mi\">13</span> <span class=\"mi\">14</span> <span class=\"mi\">15</span>]\n\n<span class=\"c1\"># Does weird things with nested lists too</span>\n> [<span class=\"mi\">1</span>, [<span class=\"mi\">2</span>, <span class=\"mi\">3</span>], <span class=\"mi\">4</span>, <span class=\"mi\">5</span>] <span class=\"s\"><<+></span>> [<span class=\"mi\">10</span>, <span class=\"mi\">20</span>]\n[<span class=\"mi\">11</span> [<span class=\"mi\">22</span> <span class=\"mi\">23</span>] <span class=\"mi\">14</span> <span class=\"mi\">25</span>]\n</code></pre></div>\n<p>Also for some reason the hyperoperators have separate behaviors on two hashes, either applying <code>op</code> to the union/intersection/hash difference. </p>\n<p>Anyway it's a super weird (meta)operator but it's also quite useful! It's the closest thing I've seen to <a href=\"https://hillelwayne.com/post/j-notation/\" target=\"_blank\">J verbs</a> outside an APL. I like using it to run the same formula on multiple possible inputs at once.</p>\n<div class=\"codehilite\"><pre><span></span><code>(<span class=\"mi\">20</span> * <span class=\"mi\">10</span> <span class=\"s\"><<-></span>> (<span class=\"mi\">21</span>, <span class=\"mi\">24</span>)) <span class=\"s\"><<*></span>> (<span class=\"mi\">10</span>, <span class=\"mi\">100</span>)\n(<span class=\"mi\">1790</span> <span class=\"mi\">17600</span>)\n</code></pre></div>\n<p>Incidentally, it's called the hyperoperator because it evaluates all of the operations in parallel. Explicit loops can be parallelized by prefixing them with <a href=\"https://docs.raku.org/language/statement-prefixes#hyper,_race\" target=\"_blank\"><code>hyper</code></a>.</p>\n<h3><a href=\"https://docs.raku.org/type/Pair\" target=\"_blank\">Pair Syntax</a></h3>\n<p>I've talked about pairs a little in <a href=\"https://buttondown.com/hillelwayne/archive/unusual-basis-types-in-programming-languages/\" target=\"_blank\">this newsletter</a>, but the gist is that Raku hashes are composed of a set of pairs <code>key => value</code>. The pair is the basis type, the hash is the collection of pairs. There's also a <em>ton</em> of syntactic sugar for concisely specifying pairs via \"colon syntax\":</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"k\">my</span> <span class=\"nv\">$x</span> = <span class=\"mi\">3</span>; :<span class=\"nv\">$x</span>\n<span class=\"nb\">x</span> => <span class=\"mi\">3</span>\n\n> :<span class=\"n\">a</span><span class=\"s\"><$x></span>\n<span class=\"n\">a</span> => <span class=\"s\">\"$x\"</span>\n\n> :<span class=\"n\">a</span>(<span class=\"nv\">$x</span>)\n<span class=\"n\">a</span> => <span class=\"mi\">3</span>\n\n> :<span class=\"mi\">3</span><span class=\"n\">a</span>\n<span class=\"n\">a</span> => <span class=\"mi\">3</span>\n</code></pre></div>\n<p>The most important sugars are <code>:key</code> and <code>:!key</code>, which map to <code>key => True</code> and <code>key => False</code>. This is a really elegant way to add flags to a methods! Take the definition of <a href=\"https://docs.raku.org/type/Str#method_match\" target=\"_blank\">match</a>:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"k\">method</span> <span class=\"nb\">match</span>(<span class=\"nv\">$pat</span>, \n :<span class=\"n\">continue</span>(:<span class=\"nv\">$c</span>), :<span class=\"n\">pos</span>(:<span class=\"nv\">$p</span>), :<span class=\"n\">global</span>(:<span class=\"nv\">$g</span>), \n :<span class=\"n\">overlap</span>(:<span class=\"nv\">$ov</span>), :<span class=\"n\">exhaustive</span>(:<span class=\"nv\">$ex</span>), \n :<span class=\"n\">st</span>(:<span class=\"nv\">$nd</span>), :<span class=\"n\">rd</span>(:<span class=\"nv\">$th</span>), :<span class=\"nv\">$nth</span>, :<span class=\"nv\">$x</span> --> <span class=\"nb\">Match</span>)\n</code></pre></div>\n<p>Probably should also mention that in a definition, <code>:f(:$foo)</code> defines the parameter <code>$foo</code> but <a href=\"https://docs.raku.org/language/signatures#Argument_aliases\" target=\"_blank\">also aliases it</a> to <code>:f</code>, so you can set the flag with <code>:f</code> or <code>:foo</code>. Colon-pairs defined in the signature can be passed in anywhere, or even stuck together:</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"s\">\"abab\"</span>.<span class=\"nb\">match</span>(<span class=\"sr\">/../</span>)\n「<span class=\"n\">ab</span>」\n> <span class=\"s\">\"abab\"</span>.<span class=\"nb\">match</span>(<span class=\"sr\">/../</span>, :<span class=\"n\">g</span>)\n(「<span class=\"n\">ab</span>」 「<span class=\"n\">ab</span>」)\n> <span class=\"s\">\"abab\"</span>.<span class=\"nb\">match</span>(<span class=\"sr\">/../</span>, :<span class=\"n\">g</span>, :<span class=\"n\">ov</span>)\n(「<span class=\"n\">ab</span>」 「<span class=\"n\">ba</span>」 「<span class=\"n\">ab</span>」)\n\n<span class=\"c1\"># Out of order stuck together</span>\n> <span class=\"s\">\"abab\"</span>.<span class=\"nb\">match</span>(:<span class=\"n\">g:ov</span>,<span class=\"sr\"> /../</span>)\n(「<span class=\"n\">ab</span>」 「<span class=\"n\">ba</span>」 「<span class=\"n\">ab</span>」)\n</code></pre></div>\n<p>So that leads to extremely concise method configuration. Definitely beats <code>match(global=True, overlap=True)</code>!</p>\n<p>And for some reason you can place keyword arguments <em>after</em> the function call:</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"s\">\"abab\"</span>.<span class=\"nb\">match</span>(:<span class=\"n\">g</span>,<span class=\"sr\"> /../</span>):<span class=\"n\">ov:2nd</span>\n「<span class=\"n\">ba</span>」\n</code></pre></div>\n<h2>The next-gen lab: Slangs and RakuAST</h2>\n<p>These are features I have no experience in and <em>certainly</em> are not making their way into other languages, but they really expand the explorable space of new features. <a href=\"https://raku.land/zef:lizmat/Slangify\" target=\"_blank\">Slangs</a> are modifications to the Raku syntax. This can be used for things like <a href=\"https://raku.land/zef:elcaro/Slang::Otherwise\" target=\"_blank\">modifying loop syntax</a>, <a href=\"https://raku.land/zef:raku-community-modules/Slang::Piersing\" target=\"_blank\">changing identifiers</a>, or adding <a href=\"https://raku.land/zef:raku-community-modules/OO::Actors\" target=\"_blank\">actors</a> or <a href=\"https://raku.land/github:MattOates/BioInfo\" target=\"_blank\">DNA sequences</a> to the base language.</p>\n<p>I <em>barely</em> understand <a href=\"https://dev.to/lizmat/rakuast-for-early-adopters-576n\" target=\"_blank\">RakuAST</a>. I <em>think</em> the idea is that all Raku expressions can be parsed as an AST from inside Raku itself.</p>\n<div class=\"codehilite\"><pre><span></span><code>> <span class=\"s\">Q/my $x; $x++/</span>.<span class=\"nb\">AST</span>\n<span class=\"n\">RakuAST::StatementList</span>.<span class=\"nb\">new</span>(\n <span class=\"n\">RakuAST::Statement::Expression</span>.<span class=\"nb\">new</span>(\n <span class=\"n\">expression</span> => <span class=\"n\">RakuAST::VarDeclaration::Simple</span>.<span class=\"nb\">new</span>(\n <span class=\"nb\">sigil</span> => <span class=\"s\">\"\\$\"</span>,\n <span class=\"n\">desigilname</span> => <span class=\"n\">RakuAST::Name</span>.<span class=\"n\">from-identifier</span>(<span class=\"s\">\"x\"</span>)\n )\n ),\n <span class=\"n\">RakuAST::Statement::Expression</span>.<span class=\"nb\">new</span>(\n <span class=\"n\">expression</span> => <span class=\"n\">RakuAST::ApplyPostfix</span>.<span class=\"nb\">new</span>(\n <span class=\"n\">operand</span> => <span class=\"n\">RakuAST::Var::Lexical</span>.<span class=\"nb\">new</span>(<span class=\"s\">\"\\$x\"</span>),\n <span class=\"nb\">postfix</span> => <span class=\"n\">RakuAST::Postfix</span>.<span class=\"nb\">new</span>(<span class=\"s\">\"++\"</span>)\n )\n )\n)\n</code></pre></div>\n<p>This allows for things like writing Raku in different languages:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"nb\">say</span> <span class=\"s\">Q/my $x; put $x/</span>.<span class=\"nb\">AST</span>.<span class=\"n\">DEPARSE</span>(<span class=\"s\">\"NL\"</span>)\n<span class=\"n\">mijn</span> <span class=\"nv\">$x</span>;\n<span class=\"n\">zeg-het</span> <span class=\"nv\">$x</span>\n</code></pre></div>\n<h3>Bonus experiment</h3>\n<p>Raku comes with a \"<a href=\"https://rakudo.org/star\" target=\"_blank\">Rakudo Star</a>\" installation, which comes with a set of <a href=\"https://github.com/rakudo/star/blob/master/etc/modules.txt\" target=\"_blank\">blessed third party modules</a> preinstalled. I love this! It's a great compromise between the maintainer burdens of a large standard library and the user burdens of making everybody find the right packages in the ecosystem.</p>\n<hr/>\n<h2>Blog Rec</h2>\n<p>Feel obligated to recommend some Raku blogs! Elizabeth Mattijsen posts <a href=\"https://dev.to/lizmat\" target=\"_blank\">a ton of stuff</a> to dev.to about Raku internals. <a href=\"https://www.codesections.com/blog/\" target=\"_blank\">Codesections</a> has a pretty good blog; he's the person who eventually got me to try out Raku. Finally, the <a href=\"https://raku-advent.blog/\" target=\"_blank\">Raku Advent Calendar</a> is a great dive into advanced Raku techniques. Bad news is it only updates once a year, good news is it's 25 updates that once a year.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:release-notes\">\n<ul>\n<li>All techniques chapters now have a \"Further Reading\" section</li>\n<li>\"System modeling\" chapter significantly rewritten</li>\n<li>\"Conditionals\" chapter expanded, now a real chapter</li>\n<li>\"Logic Programming\" chapter now covers datalog, deductive databases</li>\n<li>\"Solvers\" chapter has diagram explaining problem</li>\n<li>Eight new exercises</li>\n<li>Tentative front cover (will probably change)</li>\n<li>Fixed some epub issues with math rendering</li>\n</ul>\n<p><a class=\"footnote-backref\" href=\"#fnref:release-notes\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:analogs\">\n<p>Analogues are <a href=\"https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala/8001065#8001065\" target=\"_blank\">Scala's underscore</a>, except unlike Scala it's a value and not syntax, and like Python's <a href=\"https://docs.python.org/3/library/constants.html#Ellipsis\" target=\"_blank\">Ellipses</a>, except it has additional semantics. <a class=\"footnote-backref\" href=\"#fnref:analogs\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:spaces\">\n<p>Spaces added so buttondown doesn't think they're tags <a class=\"footnote-backref\" href=\"#fnref:spaces\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/five-unusual-raku-features/", "published": "2024-11-12T20:06:55.000Z", "updated": "2024-11-12T20:06:55.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/", "title": "A list of ternary operators", "description": "<p>Sup nerds, I'm back from SREcon! I had a blast, despite knowing nothing about site reliability engineering and being way over my head in half the talks. I'm trying to catch up on <a href=\"https://leanpub.com/logic/\" target=\"_blank\">The Book</a> and contract work now so I'll do something silly here: ternary operators.</p>\n<p>Almost all operations on values in programming languages fall into one of three buckets: </p>\n<ol>\n<li><strong>Unary operators</strong>, where the operator goes <em>before</em> or <em>after</em> exactly one argument. Examples are <code>x++</code> and <code>-y</code> and <code>!bool</code>. Most languages have a few critical unary operators hardcoded into the grammar. They are almost always symbols, but sometimes are string-identifiers (<code>not</code>).</li>\n<li><strong>Binary operators</strong>, which are placed <em>between</em> exactly two arguments. Things like <code>+</code> or <code>&&</code> or <code>>=</code>. Languages have a lot more of these than unary operators, because there's more fundamental things we want to do with two values than one value. These can be symbols or identifiers (<code>and</code>).</li>\n<li>Functions/methods that <em>prefix</em> any number of arguments. <code>func(a, b, c)</code>, <code>obj.method(a, b, c, d)</code>, anything in a lisp. These are how we extend the language, and they almost-exclusively use identifiers and not symbols.<sup id=\"fnref:lisp\"><a class=\"footnote-ref\" href=\"#fn:lisp\">1</a></sup></li>\n</ol>\n<p>There's one widespread exception to this categorization: the <strong>ternary operator</strong> <code>bool ? x : y</code>.<sup id=\"fnref:ternary\"><a class=\"footnote-ref\" href=\"#fn:ternary\">2</a></sup> It's an infix operator that takes exactly <em>three</em> arguments and can't be decomposed into two sequential binary operators. <code>bool ? x</code> makes no sense on its own, nor does <code>x : y</code>. </p>\n<p>Other ternary operators are <em>extremely</em> rare, which is why conditional expressions got to monopolize the name \"ternary\". But I like how exceptional they are and want to compile some of them. A long long time ago I asked <a href=\"https://twitter.com/hillelogram/status/1378509881498603527\" target=\"_blank\">Twitter</a> for other ternary operators; this is a compilation of some applicable responses plus my own research.</p>\n<p>(Most of these are a <em>bit</em> of a stretch.)</p>\n<h3>Stepped Ranges</h3>\n<p>Many languages have some kind of \"stepped range\" function:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\"># Python</span>\n<span class=\"o\">>>></span> <span class=\"nb\">list</span><span class=\"p\">(</span><span class=\"nb\">range</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">10</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">))</span>\n<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">,</span> <span class=\"mi\">7</span><span class=\"p\">,</span> <span class=\"mi\">9</span><span class=\"p\">]</span>\n</code></pre></div>\n<p>There's the \"base case\" of start and endpoints, and an optional step. Many languages have a binary infix op for the base case, but a few also have a ternary for the optional step:</p>\n<div class=\"codehilite\"><pre><span></span><code># Frink\n> map[{|a| a*2}, (1 to 100 step 15) ] \n[2, 32, 62, 92, 122, 152, 182]\n\n# Elixir\n> IO.puts Enum.join(1..10//2, \" \")\n1 3 5 7 9\n</code></pre></div>\n<p>This isn't decomposable into two binary ops because you can't assign the range to a value and then step the value later.</p>\n<h3>Graph ops</h3>\n<p>In <a href=\"https://graphviz.org/\" target=\"_blank\">Graphviz</a>, a basic edge between two nodes is either the binary <code>node1 -> node2</code> or the ternary <code>node1 -> node2 [edge_props]</code>:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"k\">digraph</span><span class=\"w\"> </span><span class=\"nt\">G</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"nt\">a1</span><span class=\"w\"> </span><span class=\"o\">-></span><span class=\"w\"> </span><span class=\"nt\">a2</span><span class=\"w\"> </span><span class=\"p\">[</span><span class=\"na\">color</span><span class=\"p\">=</span><span class=\"s2\">\"green\"</span><span class=\"p\">]</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p><img alt=\"Output of the above graphviz\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/d1a0f894-59d5-45d3-8702-967e94672371.png?w=960&fit=max\"/></p>\n<p>Graphs seem ternary-friendly because there are three elements involved with any graph connection: the two nodes and the connecting edge. So you also see ternaries in some graph database query languages, with separate places to specify each node and the edge.</p>\n<div class=\"codehilite\"><pre><span></span><code># GSQL (https://docs.tigergraph.com/gsql-ref/4.1/tutorials/gsql-101/parameterized-gsql-query)\nSELECT tgt\n FROM start:s -(Friendship:e)- Person:tgt;\n\n# Cypher (https://neo4j.com/docs/cypher-manual/current/introduction/cypher-overview/)\nMATCH (actor:Actor)-[:ACTED_IN]->(movie:Movie {title: 'The Matrix'})\n</code></pre></div>\n<p>Obligatory plug for my <a href=\"https://www.hillelwayne.com/post/graph-types/\" target=\"_blank\">graph datatype essay</a>.</p>\n<h3>Metaoperators</h3>\n<p>Both <a href=\"https://raku.org/\" target=\"_blank\">Raku</a> and <a href=\"https://www.jsoftware.com/#/README\" target=\"_blank\">J</a> have special higher-order functions that apply to binary infixes. Raku calls them <em>metaoperators</em>, while J calls them <em>adverbs</em> and <em>conjugations</em>.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\"># Raku</span>\n\n<span class=\"c1\"># `a «op» b` is map, \"cycling\" shorter list</span>\n<span class=\"nb\">say</span> <span class=\"s\"><10 20 30></span> «+» <span class=\"s\"><4 5></span>\n(<span class=\"mi\">14</span> <span class=\"mi\">25</span> <span class=\"mi\">34</span>)\n\n<span class=\"c1\"># `a Rop b` is `b op a`</span>\n<span class=\"nb\">say</span> <span class=\"mi\">2</span> <span class=\"n\">R-</span> <span class=\"mi\">3</span>\n<span class=\"mi\">1</span>\n</code></pre></div>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\">NB. J</span>\n\n<span class=\"c1\">NB. x f/ y creates a \"table\" of x f y</span>\n<span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"o\">+/</span><span class=\"w\"> </span><span class=\"mi\">10</span><span class=\"w\"> </span><span class=\"mi\">20</span>\n<span class=\"mi\">11</span><span class=\"w\"> </span><span class=\"mi\">21</span>\n<span class=\"mi\">12</span><span class=\"w\"> </span><span class=\"mi\">22</span>\n</code></pre></div>\n<p>The Raku metaoperators are closer to what I'm looking for, since I don't think you can assign the \"created operator\" directly to a callable variable. J lets you, though!</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"w\"> </span><span class=\"nv\">h</span><span class=\"w\"> </span><span class=\"o\">=:</span><span class=\"w\"> </span><span class=\"o\">+/</span>\n<span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"nv\">h</span><span class=\"w\"> </span><span class=\"mi\">3</span><span class=\"w\"> </span><span class=\"mi\">4</span>\n<span class=\"mi\">4</span><span class=\"w\"> </span><span class=\"mi\">5</span>\n<span class=\"mi\">5</span><span class=\"w\"> </span><span class=\"mi\">6</span>\n</code></pre></div>\n<p>That said, J has some \"decomposable\" ternaries that feel <em>spiritually</em> like ternaries, like <a href=\"https://code.jsoftware.com/wiki/Vocabulary/curlyrt#dyadic\" target=\"_blank\">amend</a> and <a href=\"https://code.jsoftware.com/wiki/Vocabulary/fcap\" target=\"_blank\">fold</a>. It also has a special ternary-ish contruct called the \"fork\".<sup id=\"fnref:ternaryish\"><a class=\"footnote-ref\" href=\"#fn:ternaryish\">3</a></sup> <code>x (f g h) y</code> is parsed as <code>(x f y) g (x h y)</code>:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\">NB. Max - min</span>\n<span class=\"w\"> </span><span class=\"mi\">5</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"o\">>.</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"o\"><.</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"mi\">2</span>\n<span class=\"mi\">3</span>\n<span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"o\">>.</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"o\"><.</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"mi\">5</span>\n<span class=\"mi\">3</span>\n</code></pre></div>\n<p>So at the top level that's just a binary operator, but the binary op is constructed via a ternary op. That's pretty cool IMO.</p>\n<h3>Assignment Ternaries</h3>\n<p>Bob Nystrom points out that in many languages, <code>a[b] = c</code> is a ternary operation: it is <em>not</em> the same as <code>x = a[b]; x = c</code>.</p>\n<p>A weirder case shows up in <a href=\"https://github.com/betaveros/noulith/\" target=\"_blank\">Noulith</a> and Raku (again): update operators. Most languages have the <code>+=</code> <em>binary operator</em>, these two have the <code>f=</code> <em>ternary operator</em>. <code>a f= b</code> is the same as <code>a = f(a, b)</code>.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\"># Raku</span>\n> <span class=\"k\">my</span> <span class=\"nv\">$x</span> = <span class=\"mi\">2</span>; <span class=\"nv\">$x</span> <span class=\"nb\">max</span>= <span class=\"mi\">3</span>; <span class=\"nb\">say</span> <span class=\"nv\">$x</span>\n<span class=\"mi\">3</span>\n</code></pre></div>\n<p>Arguably this is just syntactic sugar, but I don't think it's decomposable into binary operations.</p>\n<h3>Custom user ternaries</h3>\n<p>Tikhon Jelvis pointed out that <a href=\"https://agda.readthedocs.io/en/v2.7.0.1/language/mixfix-operators.html\" target=\"_blank\">Agda</a> lets you define <em>custom</em> mixfix operators, which can be ternary or even tetranary or pentanary. I later found out that <a href=\"https://docs.racket-lang.org/mixfix/index.html\" target=\"_blank\">Racket</a> has this, too. <a href=\"https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html\" target=\"_blank\">Objective-C</a> <em>looks</em> like this, too, but feels different somehow. </p>\n<h3>Near Misses</h3>\n<p>All of these are arguable, I've just got to draw a line in the sand <em>somewhere</em>.</p>\n<ul>\n<li>Regular expression substitutions: <code>s/from/to/flags</code> seems like a ternary, but I'd argue it a datatype constructor, not an expression operator.</li>\n<li>Comprehensions like <code>[x + 1 | x <- list]</code>: looks like the ternary <code>[expr1 | expr2 <- expr3]</code>, but <code>expr2</code> is only binding a name. Arguably a ternary if you can map <em>and filter</em> in the same expression a la Python or Haskell, but should that be considered sugar for</li>\n<li>Python's operator chaining (<code>1 < x < 5</code>): syntactic sugar for <code>1 < x and x < 5</code>.</li>\n<li>Someone suggested <a href=\"https://stackoverflow.com/questions/7251772/what-exactly-constitutes-swizzling-in-opengl-es-2-0-powervr-sgx-specifically\" target=\"_blank\">glsl swizzles</a>, which are very cool but binary operators.</li>\n</ul>\n<h2>Why are ternaries so rare?</h2>\n<p>Ternaries are <em>somewhat</em> more common in math and physics, f.ex in integrals and sums. That's because they were historically done on paper, where you have a 2D canvas, so you can do stuff like this easily:</p>\n<div class=\"codehilite\"><pre><span></span><code>10\nΣ n\nn=0\n</code></pre></div>\n<p>We express the ternary by putting arguments above and below the operator. All mainstream programming languages are linear, though, so any given symbol has only two sides. Plus functions are more regular and universal than infix operators so you might as well write <code>Sum(n=0, 10, n)</code>. The conditional ternary slips through purely because it's just so darn useful. Though now I'm wondering where it comes from in the first place. Different newsletter, maybe.</p>\n<p>But I still find ternary operators super interesting, please let me know if you know any I haven't covered!</p>\n<hr/>\n<h3>Blog Rec</h3>\n<p>This week's blog rec is <a href=\"https://lexi-lambda.github.io/\" target=\"_blank\">Alexis King</a>! Generally, Alexis's work spans the theory, practice, and implementation of programming languages, aimed at a popular audience and not an academic one. If you know her for one thing, it's probably <a href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate\" target=\"_blank\">Parse, don't validate</a>, which is now so mainstream most people haven't read the original post. Another good one is about <a href=\"https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/\" target=\"_blank\">modeling open-world systems with static types</a>. </p>\n<p>Nowadays she is <em>far</em> more active on <a href=\"https://langdev.stackexchange.com/users/861/alexis-king\" target=\"_blank\">Programming Languages Stack Exchange</a>, where she has blog-length answers on <a href=\"https://langdev.stackexchange.com/questions/2692/how-should-i-read-type-system-notation/2693#2693\" target=\"_blank\">reading type notations</a>, <a href=\"https://langdev.stackexchange.com/questions/3942/what-are-the-ways-compilers-recognize-complex-patterns/3945#3945\" target=\"_blank\">compiler design</a>, and <a href=\"https://langdev.stackexchange.com/questions/2069/what-is-an-arrow-and-what-powers-would-it-give-as-a-first-class-concept-in-a-pro/2372#2372\" target=\"_blank\">why arrows</a>.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:lisp\">\n<p>Unless it's a lisp. <a class=\"footnote-backref\" href=\"#fnref:lisp\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:ternary\">\n<p>Or <code>x if bool else y</code>, same thing. <a class=\"footnote-backref\" href=\"#fnref:ternary\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:ternaryish\">\n<p>I say \"ish\" because trains can be arbitrarily long: <code>x (f1 f2 f3 f4 f5) y</code> is something I have <em>no idea</em> <a href=\"https://code.jsoftware.com/wiki/Vocabulary/fork\" target=\"_blank\">how to parse</a>. <a class=\"footnote-backref\" href=\"#fnref:ternaryish\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/a-list-of-ternary-operators/", "published": "2024-11-05T18:40:33.000Z", "updated": "2024-11-05T18:40:33.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/tla-from-first-principles/", "title": "TLA from first principles", "description": "<h3>No Newsletter next week</h3>\n<p>I'll be speaking at <a href=\"https://www.usenix.org/conference/srecon24emea/presentation/wayne\" target=\"_blank\">USENIX SRECon</a>!</p>\n<h2>TLA from first principles</h2>\n<p>I'm working on v0.5 of <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Logic for Programmers</a>. In the process of revising the \"System Modeling\" chapter, I stumbled on a great way to explain the <strong>T</strong>emporal <strong>L</strong>ogic of <strong>A</strong>ctions that TLA+ is based on. I'm reproducing that bit here with some changes to fit the newsletter format.</p>\n<p>Note that by this point the reader has already encountered property testing, formal verification, decision tables, and nontemporal specifications, and should already have a lot of practice expressing things as predicates. </p>\n<hr/>\n<h3>The intro</h3>\n<p>We have some bank users, each with an account balance. Bank users can wire money\nto each other. We have overdraft protection, so wires cannot reduce an\naccount value below zero. </p>\n<p>For the purposes of introducing the ideas, we'll assume an extremely simple system: two hardcoded\nvariables <code>alice</code> and <code>bob</code>, both start with 10 dollars, and transfers\nare only from Alice to Bob. Also, the transfer is totally atomic: we\ncheck for adequate funds, withdraw, and deposit all in a single moment\nof time. Later [in the chapter] we'll allow for multiple nonatomic transfers at the same time.</p>\n<p>First, let's look at a valid <strong>behavior</strong> of the system, or possible way it can evolve.</p>\n<div class=\"codehilite\"><pre><span></span><code>alice 10 -> 5 -> 3 -> 3 -> ...\nbob 10 -> 15 -> 17 -> 17 -> ...\n</code></pre></div>\n<p>In programming, we'd think of <code>alice</code> and <code>bob</code> as variables that change. How do we represent those variables <em>purely</em> in terms of predicate logic? One way is to instead think of them as <em>arrays</em> of values. <code>alice[0]</code> is the initial state of <code>alice</code>, <code>alice[1]</code> is after the first time step, etc. Time, then, is \"just\" the set of natural numbers.</p>\n<div class=\"codehilite\"><pre><span></span><code>Time = {0, 1, 2, 3, ...}\nalice = [10, 5, 3, 3, ...]\nbob = [10, 15, 17, 17, ...]\n</code></pre></div>\n<p>In comparison to our valid behavior, here are some <em>invalid</em> behaviors:</p>\n<div class=\"codehilite\"><pre><span></span><code>alice = [10, 3, ...]\nbob = [10 15, ...]\n\nalice = [10, -1, ...]\nbob = [10 21, ...]\n</code></pre></div>\n<p>The first is invalid because Bob received more money than Alice lost.\nThe second is invalid because it violates our proposed invariant, that\naccounts cannot go negative. Can we write a predicate that is <em>true</em> for\nvalid transitions and <em>false</em> for our two invalid behaviors?</p>\n<p>Here's one way:</p>\n<div class=\"codehilite\"><pre><span></span><code>Time = Nat // {0, 1, 2, etc}\n\nTransfer(t: Time) =\n some value in 0..=alice[t]:\n 1. alice[t+1] = alice[t] - value\n 2. bob[t+1] = bob[t] + value\n</code></pre></div>\n<p>Go through and check that this is true for every <code>t</code> in the valid\nbehavior and false for at least one <code>t</code> in the invalid behavior. Note\nthat the steps where Alice <em>doesn't</em> send a transfer also pass\n<code>Transfer</code>; we just pick <code>value = 0</code>.</p>\n<p>I can now write a predicate that perfectly describes a valid behavior:</p>\n<div class=\"codehilite\"><pre><span></span><code>Spec = \n 1. alice[0] = 10\n 2. bob[0] = 10\n 3. all t in Time:\n Transfer(t)\n</code></pre></div>\n<p>Now allowing \"nothing happens\" as \"Alice sends an empty transfer\" is\na little bit weird. In the real system, we probably don't want people\nto constantly be sending each other zero dollars:</p>\n<div class=\"codehilite\"><pre><span></span><code>Transfer(t: Time) =\n<span class=\"gd\">- some value in 0..=alice[t]:</span>\n<span class=\"gi\">+ some value in 1..=alice[t]:</span>\n<span class=\"w\"> </span> 1. alice[t+1] = alice[t] - value\n<span class=\"w\"> </span> 2. bob[t+1] = bob[t] + value\n</code></pre></div>\n<p>But now there can't be a timestep where nothing happens. And that means\n<em>no</em> behavior is valid! At every step, Alice <em>must</em> transfer at least one dollar to Bob.\nEventually there is some <code>t</code> where <code>alice[t] = 0 && bob[t] = 20</code>. Then\nAlice can't make a transfer, <code>Transfer(t)</code> is false, and so <code>Spec</code> is\nfalse.<sup id=\"fnref:exercise\"><a class=\"footnote-ref\" href=\"#fn:exercise\">1</a></sup></p>\n<p>So typically when modeling we add a <strong>stutter step</strong>, like this:</p>\n<div class=\"codehilite\"><pre><span></span><code>Spec =\n 1. alice[0] = 10\n 2. bob[0] = 10\n 3. all t in Time:\n || Transfer(t)\n || 1. alice[t+1] = alice[t]\n 2. bob[t+1] = bob[t]\n</code></pre></div>\n<p>(This is also why we can use infinite behaviors to model a finite algorithm. If the algorithm completes at <code>t=21</code>, <code>t=22,23,24...</code> are all stutter steps.)</p>\n<p>There's enough moving parts here that I'd want to break it into\nsubpredicates.</p>\n<div class=\"codehilite\"><pre><span></span><code>Init =\n 1. alice[0] = 10\n 2. bob[0] = 10\n\nStutter(t) =\n 1. alice[t+1] = alice[t]\n 2. bob[t+1] = bob[t]\n\nNext(t) = Transfer(t) // foreshadowing\n\nSpec =\n 1. Init\n 2. all t in Time:\n Next(t) || Stutter(t)\n</code></pre></div>\n<p>Now finally, how do we represent the property <code>NoOverdrafts</code>? It's an\n<em>invariant</em> that has to be true at all times. So we do the same thing we\ndid in <code>Spec</code>, write a predicate over all times.</p>\n<div class=\"codehilite\"><pre><span></span><code>property NoOverdrafts =\n all t in Time:\n alice[t] >= 0 && bob[t] >= 0\n</code></pre></div>\n<p>We can even say that <code>Spec => NoOverdrafts</code>, ie if a behavior is valid\nunder <code>Spec</code>, it satisfies <code>NoOverdrafts</code>.</p>\n<h4>One of the exercises</h4>\n<p>Modify the <code>Next</code> so that Bob can send Alice transfers, too. Don't try\nto be too clever, just do this in the most direct way possible.</p>\n<p>Bonus: can Alice and Bob transfer to each other in the same step?</p>\n<p><strong>Solution</strong> [in back of book]: We can rename <code>Transfer(t)</code> to <code>TransferAliceToBob(t)</code>, write the\nconverse as a new predicate, and then add it to <code>next</code>. Like this</p>\n<div class=\"codehilite\"><pre><span></span><code>TransferBobToAlice(t: Time) =\n some value in 1..=bob[t]:\n 1. alice[t+1] = alice[t] - value\n 2. bob[t+1] = bob[t] + value\n\nNext(t) =\n || TransferAliceToBob(t)\n || TransferBobToAlice(t)\n</code></pre></div>\n<p>Now, can Alice and Bob transfer to each other in the same step? No.\nLet's say they both start with 10 dollars and each try to transfer five\ndollars to each other. By <code>TransferAliceToBob</code> we have:</p>\n<div class=\"codehilite\"><pre><span></span><code>1. alice[1] = alice[0] - 5 = 5\n2. bob[1] = bob[0] + 5 = 15\n</code></pre></div>\n<p>And by <code>TransferBobToAlice</code>, we have:</p>\n<div class=\"codehilite\"><pre><span></span><code>1. bob[1] = bob[0] - 5 = 5\n2. alice[1] = alice[0] + 5 = 15\n</code></pre></div>\n<p>So now we have <code>alice[1] = 5 && alice[1] = 15</code>, which is always false.</p>\n<h3>Temporal Logic</h3>\n<div class=\"subscribe-form\"></div>\n<p>This is good and all, but in practice, there's two downsides to\ntreating time as a set we can quantify over:</p>\n<ol>\n<li>It's cumbersome. We have to write <code>var[t]</code> and <code>var[t+1]</code> all over\n the place.</li>\n<li>It's too powerful. We can write expressions like\n <code>alice[t^2-5] = alice[t] + t</code>.</li>\n</ol>\n<p>Problem (2) might seem like a good thing; isn't the whole <em>point</em> of\nlogic to be expressive? But we have a long-term goal in mind: getting a\ncomputer to check our formal specification. We need to limit the\nexpressivity of our model so that we can make it checkable. </p>\n<p>In practice, this will mean making time implicit to our model, instead of\nexplicitly quantifying over it.</p>\n<p>The first thing we need to do is limit how we can use time. At a\ngiven point in time, all we can look at is the <em>current</em> value of a\nvariable (<code>var[t]</code>) and the <em>next</em> value (<code>var[t+1]</code>). No <code>var[t+16]</code> or\n<code>var[t-1]</code> or anything else complicated.</p>\n<p>And it turns out we've already seen a mathematical convention for\nexpressing this: <strong>priming</strong>!<sup id=\"fnref:priming\"><a class=\"footnote-ref\" href=\"#fn:priming\">2</a></sup> For a\ngiven time <code>t</code>, we can define <code>var</code> to mean <code>var[t]</code> and <code>var'</code> to mean\n<code>var[t+1]</code>. Then <code>Transfer(t)</code> becomes</p>\n<div class=\"codehilite\"><pre><span></span><code>Transfer =\n some value in 1..=alice:\n 1. alice' = alice\n 2. bob' = bob\n</code></pre></div>\n<p>Next we have the construct <code>all t in Time: P(t)</code> in both <code>Spec</code> and\n<code>NoOverdrafts</code>. In other words, \"P is always true\". So we can add\n<code>always</code> as a new term. Logicians conventionally use □ or <code>[]</code>\nto mean the same thing.<sup id=\"fnref:beyond\"><a class=\"footnote-ref\" href=\"#fn:beyond\">3</a></sup></p>\n<div class=\"codehilite\"><pre><span></span><code>property NoOverdrafts =\n always (alice >= 0 && bob[t] >= 0)\n // or [](alice >= 0 && bob[t] >= 0)\n\nSpec =\n Init && always (Next || Stutter)\n</code></pre></div>\n<p>Now time is <em>almost</em> completely implicit in our spec, with just one\nexception: <code>Init</code> has <code>alice[0]</code> and <code>bob[0]</code>. We just need one more\nconvention: if a variable is referenced <em>outside</em> of the scope of a\ntemporal operator, it means <code>var[0]</code>. Since <code>Init</code> is outside of the <code>[]</code>, it becomes</p>\n<div class=\"codehilite\"><pre><span></span><code>Init =\n 1. alice = 10\n 2. bob = 10\n</code></pre></div>\n<p>And with that, we've removed <code>Time</code> as an explicit value in our model.</p>\n<p>The addition of primes and <code>always</code> makes this a <strong>temporal logic</strong>, one that can model how things change over time. And that makes it ideal for modeling software systems.</p>\n<h3>Modeling with TLA+</h3>\n<p>One of the most popular specification languages for modeling these kinds\nof concurrent systems is <strong>TLA+</strong>. TLA+ was invented by the Turing award-winner Leslie Lamport, who also invented a wide variety of concurrency algorithms and LaTeX. Here's our current\nspec in TLA+:</p>\n<div class=\"codehilite\"><pre><span></span><code>---- MODULE transfers ----\nEXTENDS Integers\n\nVARIABLES alice, bob\nvars == <<alice, bob>>\n\nInit ==\n alice = 10 \n /\\ bob = 10\n\nAliceToBob ==\n \\E amnt \\in 1..alice:\n alice' = alice - amnt\n /\\ bob' = bob + amnt\n\nBobToAlice ==\n \\E amnt \\in 1..bob:\n alice' = alice + amnt\n /\\ bob' = bob - amnt\n\nNext ==\n AliceToBob\n \\/ BobToAlice\n\nSpec == Init /\\ [][Next]_vars\n\nNoOverdrafts ==\n [](alice >= 0 /\\ bob >= 0)\n\n====\n</code></pre></div>\n<p>TLA+ uses ASCII versions of mathematicians notation: <code>/\\</code>/<code>\\/</code> for\n<code>&&/||</code>, <code>\\A \\E</code> for <code>all/some</code>, etc. The only thing that's \"unusual\"\n(besides <code>==</code> for definition) is the <code>[][Next]_vars</code> bit. That's TLA+\nnotation for <code>[](Next || Stutter)</code>: <code>Next</code> or <code>Stutter</code> always happens.</p>\n<hr/>\n<p>The rest of the chapter goes on to explain model checking, PlusCal (for modeling nonatomic transactions without needing to explain the exotic TLA+ function syntax), and liveness properties. But this is the intuition behind the \"temporal logic of actions\": temporal operators are operations on the set of points of time, and we restrict what we can do with those operators to make reasoning about the specification feasible.</p>\n<p>Honestly I like it enough that I'm thinking of redesigning my TLA+ workshop to start with this explanation. Then again, maybe it only seems good to me because I already know TLA+. Please let me know what you think about it!</p>\n<p>Anyway, the new version of the chapter will be in v0.5, which should be out mid-November.</p>\n<hr/>\n<h3>Blog Rec</h3>\n<p>This one it's really dear to me: <a href=\"https://muratbuffalo.blogspot.com/\" target=\"_blank\">Metadata</a>, by Murat Demirbas. When I was first trying to learn TLA+ back in 2016, his post <a href=\"https://muratbuffalo.blogspot.com/2015/01/my-experience-with-using-tla-in.html\" target=\"_blank\">on using TLA+ in a distributed systems class</a> was one of, like... <em>three</em> public posts on TLA+. I must have spent hours rereading that post and puzzling out this weird language I stumbled into. Later I emailed Murat with some questions and he was super nice in answering them. Don't think I would have ever grokked TLA+ without him.</p>\n<p>In addition to TLA+ content, a lot of the blog is also breakdowns of papers he read— like <a href=\"https://blog.acolyer.org/\" target=\"_blank\">the morning paper</a>, except with a focus on distributed systems (and still active). If you're interested in learning more about the science of distributed systems, he has an excellent page on <a href=\"https://muratbuffalo.blogspot.com/2021/02/foundational-distributed-systems-papers.html\" target=\"_blank\">foundational distributed systems papers</a>. But definitely check out his <a href=\"https://muratbuffalo.blogspot.com/2023/09/metastable-failures-in-wild.html\" target=\"_blank\">his deep readings</a>, too!</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:exercise\">\n<p>In the book this is presented as an exercise (with the solution in back). The exercise also clarifies that since <code>Time = Nat</code>, all behaviors have an <em>infinite</em> number of steps. <a class=\"footnote-backref\" href=\"#fnref:exercise\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:priming\">\n<p>Priming is introduced in the chapter on decision tables, and again in the chapter on database invariants. <code>x'</code> is \"the next value of <code>x</code>\", so you can use it to express database invariants like \"jobs only move from <code>ready</code> to <code>started</code> or <code>aborted</code>.\" <a class=\"footnote-backref\" href=\"#fnref:priming\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:beyond\">\n<p>I'm still vacillating on whether I want a \"beyond logic\" appendix that covers higher order logic, constructive logic, and modal logic (which is what we're sneakily doing right now!)</p>\n<p>While I'm here, this explanation of <code>always</code> as <code>all t in Time</code> isn't <em>100%</em> accurate, since it doesn't explain why things like <code>[](P => []Q)</code> or <code><>[]P</code> make sense. But it's accurate in most cases and is a great intuition pump. <a class=\"footnote-backref\" href=\"#fnref:beyond\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/tla-from-first-principles/", "published": "2024-10-22T17:14:21.000Z", "updated": "2024-10-22T17:14:21.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/", "title": "Be Suspicious of Success", "description": "<p>From Leslie Lamport's <em>Specifying Systems</em>:</p>\n<blockquote>\n<p>You should be suspicious if [the model checker] does not find a violation of a liveness property... you should also be suspicious if [it] finds no errors when checking safety properties. </p>\n</blockquote>\n<p>This is specifically in the context of model-checking a formal specification, but it's a widely applicable software principle. It's not enough for a program to work, it has to work for the <em>right reasons</em>. Code working for the wrong reasons is code that's going to break when you least expect it. And since \"correct for right reasons\" is a much narrower target than \"correct for any possible reason\", we can't assume our first success is actually our intended success.</p>\n<p>Hence, BSOS: <strong>Be Suspicious of Success</strong>.</p>\n<h3>Some useful BSOS practices</h3>\n<p>The standard way of dealing with BSOS is verification. Tests, static checks, model checking, etc. We get more confident in our code if our verifications succeed. But then we also have to be suspicious of <em>that</em> success, too! How do I know whether my tests are passing because they're properly testing correct code or because they're failing to test incorrect code?</p>\n<p>This is why test-driven development gurus tell people to write a failing test first. Then at least we know the tests are doing <em>something</em> (even if they still might not be testing what they want).</p>\n<p>The other limit of verification is that it can't tell us <em>why</em> something succeeds. Mainstream verification methods are good at explaining why things <em>fail</em>— expected vs actual test output, type mismatches, specification error traces. Success isn't as \"information-rich\" as failure. How do you distinguish a faithful implementation of <a href=\"https://en.wikipedia.org/wiki/Collatz_conjecture\" target=\"_blank\"><code>is_collatz_counterexample</code></a> from <code>return false</code>?</p>\n<p>A broader technique I follow is <em>make it work, make it break</em>. If code is working for the right reasons, I should be able to predict how to break it. This can be either a change in the runtime (this will livelock if we 10x the number of connections), or a change to the code itself (commenting out <em>this</em> line will cause property X to fail). <sup id=\"fnref:superproperties\"><a class=\"footnote-ref\" href=\"#fn:superproperties\">1</a></sup> If the code still works even after the change, my model of the code is wrong and it was succeeding for the wrong reasons.</p>\n<h3>Happy and Sad Paths</h3>\n<div class=\"subscribe-form\"></div>\n<p>A related topic (possibly subset?) is \"happy and sad paths\". The happy path of your code is the behavior when everything's going right: correct inputs, preconditions are satisfied, the data sources are present, etc. The sad path is all of the code that handles things going wrong. Retry mechanisms, insufficient user authority, database constraint violation, etc. In most software, the code supporting the sad paths dwarfs the code in the happy path.</p>\n<p>BSOS says that I can't just show code works in the happy path, I also need to check it works in the sad path. </p>\n<p>BSOS also says that I have to be suspicious when the sad path works properly, too. </p>\n<p>Say I add a retry mechanism to my code to handle the failure mode of timeouts. I test the code and it works. Did the retry code actually <em>run</em>? Did it run <em>regardless</em> of the original response? Is it really doing exponential backoff? Will stop after the maximum retry limit? Is the sad path code <em>after</em> the maximum retry limit working properly?</p>\n<p><a href=\"https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf\" target=\"_blank\">One paper</a> found that 35% of catastrophic distributed system failures were caused by \"trivial mistakes in error handlers\" (pg 9). These were in mature, battle-hardened programs. Be suspicious of success. Be more suspicious of sad path success.</p>\n<hr/>\n<h2>Blog Rec</h2>\n<p>This week's blog rec is <a href=\"https://www.redblobgames.com/\" target=\"_blank\">Red Blob Games</a>!<sup id=\"fnref:blogs-vs-articles\"><a class=\"footnote-ref\" href=\"#fn:blogs-vs-articles\">2</a></sup> While primarily about computer game programming, the meat of the content is beautiful, interactive guides to general CS algorithms. Some highlights:</p>\n<ul>\n<li><a href=\"https://www.redblobgames.com/pathfinding/a-star/introduction.html\" target=\"_blank\">Introduction to the A* Algorithm</a> was really illuminating when I was a baby programmer.</li>\n<li>I'm sure this <a href=\"https://www.redblobgames.com/articles/noise/introduction.html\" target=\"_blank\">overview of noise functions</a> will be useful to me <em>someday</em>. Maybe for test data generation?</li>\n<li>If you're also an explainer type he has a lot of great stuff on <a href=\"https://www.redblobgames.com/making-of/line-drawing/\" target=\"_blank\">his process</a> and his <a href=\"https://www.redblobgames.com/making-of/little-things/\" target=\"_blank\">little tricks</a> to make things more understandable.</li>\n</ul>\n<p>(I don't think his <a href=\"https://www.redblobgames.com/blog/posts.xml\" target=\"_blank\">rss feed</a> covers new interactive articles, only the <a href=\"https://www.redblobgames.com/blog/\" target=\"_blank\">blog</a> specifically.)</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:superproperties\">\n<p><a href=\"https://www.jameskoppel.com/\" target=\"_blank\">Jimmy Koppel</a> once proposed that just as code has properties, code variations have <a href=\"https://groups.csail.mit.edu/sdg/pubs/2020/demystifying_dependence_published.pdf\" target=\"_blank\"><strong>superproperties</strong></a>. For example, \"no modification to the codebase causes us to use a greater number of deprecated APIs.\" <a class=\"footnote-backref\" href=\"#fnref:superproperties\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:blogs-vs-articles\">\n<p>Okay, it's more an <em>article</em> site, because there's also a <a href=\"https://www.redblobgames.com/blog/\" target=\"_blank\">Red Blob <em>blog</em></a> (which covers a lot of neat stuff, too). Maybe I should just rename this section to \"site rec\". <a class=\"footnote-backref\" href=\"#fnref:blogs-vs-articles\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/be-suspicious-of-success/", "published": "2024-10-16T15:08:39.000Z", "updated": "2024-10-16T15:08:39.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/", "title": "How to convince engineers that formal methods is cool", "description": "<p>Sorry there was no newsletter last week! I got COVID. Still got it, which is why this one's also short.</p>\n<h3>Logic for Programmers v0.4</h3>\n<p><a href=\"https://leanpub.com/logic/\" target=\"_blank\">Now available</a>! This version adds a chapter on TLA+, significantly expands the constraint solver chapter, and adds a \"planner programming\" section to the Logic Programming chapter. You can see the full release notes on the <a href=\"https://leanpub.com/logic/\" target=\"_blank\">book page</a>.</p>\n<h1>How to convince engineers that formal methods is cool</h1>\n<p>I have an open email for answering questions about formal methods,<sup id=\"fnref:fs-fv\"><a class=\"footnote-ref\" href=\"#fn:fs-fv\">1</a></sup> and one of the most common questions I get is \"how do I convince my coworkers that this is worth doing?\" usually the context is the reader is really into the idea of FM but their coworkers don't know it exists. The goal of the asker is to both introduce FM and persuade them that FM's useful. </p>\n<p>In my experience as a consultant and advocate, I've found that there's only two consistently-effective ways to successfully pitch FM:</p>\n<ol>\n<li>Use FM to find an <em>existing</em> bug in a work system</li>\n<li>Show how FM finds a historical bug that's already been fixed.</li>\n</ol>\n<h4>Why this works</h4>\n<p>There's two main objections to FM that we need to address. The first is that FM is too academic and doesn't provide a tangible, practical benefit. The second is that FM is too hard; only PhDs and rocket scientists can economically use it. (Showing use cases from AWS <em>et al</em> aren't broadly persuasive because skeptics don't have any insight into how AWS functions.) Finding an existing bug hits both: it helped the team with a real problem, and it was done by a mere mortal. </p>\n<div class=\"subscribe-form\"></div>\n<p>Demonstrating FM on a historical bug isn't <em>as</em> effective: it only shows that formal methods <em>could have</em> helped, not that it actually does help. But people will usually remember the misery of debugging that problem. Bug war stories are popular for a reason!</p>\n<h3>Making historical bugs persuasive</h3>\n<p>So \"live bug\" is a stronger rec, but \"historical bug\" tends to be easier to show. This is because <em>you know what you're looking for</em>. It's easier to write a high-level spec on a system you already know, and show it finds a bug you already know about.</p>\n<p>The trick to make it look convincing is to make the spec and bug as \"natural\" as possible. You can't make it seem like FM only found the bug because you had foreknowledge of what it was— then the whole exercise is too contrived. People will already know you had foreknowledge, of course, and are factoring that into their observations. You want to make the case that the spec you're writing is clear and obvious enough that an \"ignorant\" person could have written it. That means nothing contrived or suspicious.</p>\n<p>This is a bit of a fuzzy definition, more a vibe than anything. Ask yourself \"does this spec look like something that was tailor-made around this bug, or does it find the bug as a byproduct of being a regular spec?\"</p>\n<p>A good example of a \"natural\" spec is <a href=\"https://www.hillelwayne.com/post/augmenting-agile/\" target=\"_blank\">the bounded queue problem</a>. It's a straight translation of some Java code with no properties besides deadlock checking. Usually you'll be at a higher level of abstraction, though.</p>\n<hr/>\n<h3>Blog rec: <a href=\"https://www.argmin.net/\" target=\"_blank\">arg min</a></h3>\n<p>This is a new section I want to try for a bit: recommending tech(/-adjacent) blogs that I like. This first one is going to be a bit niche: <a href=\"https://www.argmin.net/\" target=\"_blank\">arg min</a> is writing up lecture notes on \"convex optimization\". It's a cool look into the theory behind constraint solving. I don't understand most of the math but the prose is pretty approachable. Couple of highlights:</p>\n<ul>\n<li><a href=\"https://www.argmin.net/p/modeling-dystopia\" target=\"_blank\">Modeling Dystopia</a> about why constraint solving isn't a mainstream technology.</li>\n<li><a href=\"https://www.argmin.net/p/convex-optimization-live-blog\" target=\"_blank\">Table of Contents</a> to see all of the posts.</li>\n</ul>\n<p>The blogger also talks about some other topics but I haven't read those posts much.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:fs-fv\">\n<p>As always, talking primarily about formal specification of systems (TLA+/Alloy/Spin), not formal verification of code (Dafny/SPARK/Agda). I talk about the differences a bit <a href=\"https://www.hillelwayne.com/post/why-dont-people-use-formal-methods/\" target=\"_blank\">here</a> (but I really need to write a more focused piece). <a class=\"footnote-backref\" href=\"#fnref:fs-fv\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/how-to-convince-engineers-that-formal-methods-is/", "published": "2024-10-08T16:18:55.000Z", "updated": "2024-10-08T16:18:55.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/refactoring-invariants/", "title": "Refactoring Invariants", "description": "<p>(Feeling a little sick so this one will be short.)</p>\n<p>I'm often asked by clients to review their (usually TLA+) formal specifications. These specs are generally slower and more convoluted than an expert would write. I want to fix them up without changing the overall behavior of the spec or introducing subtle bugs.</p>\n<p>To do this, I use a rather lovely feature of TLA+. Say I see a 100-line <code>Foo</code> action that I think I can refactor down to 20 lines. I'll first write a refactored version as a separate action <code>NewFoo</code>, then I run the model checker with the property</p>\n<div class=\"codehilite\"><pre><span></span><code>RefactorProp ==\n [][Foo <=> NewFoo]_vars\n</code></pre></div>\n<p>That's an intimidating nest of symbols but all it's saying is that every <code>Foo</code> step must also be a <code>NewFoo</code> step. If the refactor ever does something different from the original action, the model-checker will report the exact behavior and transition it fails for. Conversely, if the model checker passes, I can safely assume they have identical behaviors.</p>\n<p>This is a <strong>refactoring invariant</strong>:<sup id=\"fnref:invariant\"><a class=\"footnote-ref\" href=\"#fn:invariant\">1</a></sup> the old and new versions of functions have identical behavior. Refactoring invariants are superbly useful in formal specification. Software devs spend enough time refactoring that they'd be useful for coding, too.</p>\n<p>Alas, refactoring invariants are a little harder to express in code. In TLA+ we're working with bounded state spaces, so the model checker can check the invariant for every possible state. Even a simple program can have an unbounded state space via an infinite number of possible function inputs. </p>\n<p>(Also formal specifications are \"pure\" simulations while programs have side effects.)</p>\n<p>The \"normal\" way to verify a program refactoring is to start out with a huge suite of <a href=\"https://buttondown.com/hillelwayne/archive/oracle-testing/\" target=\"_blank\">oracle tests</a>. This <em>should</em> catch a bad refactor via failing tests. The downside is that you might not have the test suite in the first place, or not one that covers your particular refactoring. Second, even if the test suite does, it only indirectly tests the invariant. It catches the refactoring error as a consequence of testing other stuff. What if we want to directly test the refactoring invariant?</p>\n<h3>Two ways of doing this</h3>\n<p>One: by pulling in formal methods. Ray Myers has a <a href=\"https://www.youtube.com/watch?v=UdB3XBf219Y\" target=\"_blank\">neat video</a> on formally proving a refactoring is correct. That one's in the niche language ACL2, but he's also got one on <a href=\"https://www.youtube.com/watch?v=_7RXQE-pCMo\" target=\"_blank\">refactoring C</a>. You might not even to prove the refactoring correct, you could probably get away with using an <a href=\"https://github.com/pschanely/CrossHair\" target=\"_blank\">SMT solver</a> to find counterexamples.</p>\n<p>Two: by using property-based testing. Generate random inputs, pass them to both functions, and check that the outputs are identical. Using the python <a href=\"https://hypothesis.readthedocs.io/en/latest/\" target=\"_blank\">Hypothesis</a> library:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kn\">from</span> <span class=\"nn\">hypothesis</span> <span class=\"kn\">import</span> <span class=\"n\">given</span>\n<span class=\"kn\">import</span> <span class=\"nn\">hypothesis.strategies</span> <span class=\"k\">as</span> <span class=\"nn\">st</span>\n\n<span class=\"c1\"># from the `gilded rose kata`</span>\n<span class=\"k\">def</span> <span class=\"nf\">update_quality</span><span class=\"p\">(</span><span class=\"nb\">list</span><span class=\"p\">[</span><span class=\"n\">Item</span><span class=\"p\">]):</span>\n <span class=\"o\">...</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">update_quality_new</span><span class=\"p\">(</span><span class=\"nb\">list</span><span class=\"p\">[</span><span class=\"n\">Item</span><span class=\"p\">]):</span>\n <span class=\"o\">...</span>\n\n<span class=\"nd\">@given</span><span class=\"p\">(</span><span class=\"n\">st</span><span class=\"o\">.</span><span class=\"n\">lists</span><span class=\"p\">(</span><span class=\"n\">st</span><span class=\"o\">.</span><span class=\"n\">builds</span><span class=\"p\">(</span><span class=\"n\">Item</span><span class=\"p\">)))</span>\n<span class=\"k\">def</span> <span class=\"nf\">test_refactoring</span><span class=\"p\">(</span><span class=\"n\">l</span><span class=\"p\">):</span>\n <span class=\"k\">assert</span> <span class=\"n\">update_quality</span><span class=\"p\">(</span><span class=\"n\">l</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"n\">update_quality_new</span><span class=\"p\">(</span><span class=\"n\">l</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>One tricky bit is if the function is part of a long call chain <code>A -> B -> C</code>, and you want to test that refactoring <code>C'</code> doesn't change the behavior of <code>A</code>. You'd have to add a <code>B'</code> that uses <code>C'</code> and then an <code>A'</code> that uses <code>B'</code>. Maybe you could instead create a branch, commit the change the <code>C'</code> in that branch, and then run a <a href=\"https://www.hillelwayne.com/post/cross-branch-testing/\" target=\"_blank\">cross-branch test</a> against each branch's <code>A</code>.</p>\n<p>Impure functions are harder. The test now makes some side effect twice, which could spuriously break the refactoring invariant. You could instead test the changes are the same, or try to get the functions to effect different entities and then compare the updates of each entity. There's no general solution here though, and there might be No Good Way for a particular effectful refactoring.</p>\n<h3>Behavior-changing rewrites</h3>\n<p>We can apply similar ideas for rewrites that change <em>behavior</em>. Say we have an API, and v1 returns a list of user names while v2 returns a <code>{version, userids}</code> dict. Then we can find some transformation of v2 into v1, and run the refactoring invariant on that:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"k\">def</span> <span class=\"nf\">v2_to_v1</span><span class=\"p\">(</span><span class=\"n\">v2_resp</span><span class=\"p\">):</span>\n <span class=\"k\">return</span> <span class=\"p\">[</span><span class=\"n\">User</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">name</span> <span class=\"k\">for</span> <span class=\"n\">user</span> <span class=\"ow\">in</span> <span class=\"n\">v2_resp</span><span class=\"p\">[</span><span class=\"s2\">\"userids\"</span><span class=\"p\">]]</span>\n\n<span class=\"nd\">@given</span><span class=\"p\">(</span><span class=\"n\">some_query_generator</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">test_refactoring</span><span class=\"p\">(</span><span class=\"n\">q</span><span class=\"p\">):</span>\n <span class=\"k\">assert</span> <span class=\"n\">v1</span><span class=\"p\">(</span><span class=\"n\">q</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"n\">v2_to_v1</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">(</span><span class=\"n\">q</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>Fun fact: <code>v2_to_v1</code> is a <a href=\"https://buttondown.com/hillelwayne/archive/software-isomorphisms/\" target=\"_blank\">software homomorphism</a>!</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:invariant\">\n<p>Well technically it's an <em>action property</em> since it's on the transitions of states, not the states, but \"refactor invariant\" gets the idea across better. <a class=\"footnote-backref\" href=\"#fnref:invariant\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/refactoring-invariants/", "published": "2024-09-24T20:06:10.000Z", "updated": "2024-09-24T20:06:10.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/", "title": "Goodhart's Law in Software Engineering", "description": "<h3>Blog Hiatus</h3>\n<p>You might have noticed I haven't been updating my website. I haven't even <em>looked</em> at any of my drafts for the past three months. All that time is instead going into <em>Logic for Programmers</em>. I'll get back to the site when that's done or in 2025, whichever comes first. Newsletter and <a href=\"https://www.patreon.com/hillelwayne\" target=\"_blank\">Patreon</a> will still get regular updates.</p>\n<p>(As a comparison, the book is now 22k words. That's like 11 blog posts!)</p>\n<h2>Goodhart's Law in Software Engineering</h2>\n<p>I recently got into an argument with some people about whether small functions were <em>mostly</em> a good idea or <em>always 100%</em> a good idea, and it reminded me a lot about <a href=\"https://en.wikipedia.org/wiki/Goodhart%27s_law\" target=\"_blank\">Goodhart's Law</a>:</p>\n<blockquote>\n<p>When a measure becomes a target, it ceases to be a good measure.</p>\n</blockquote>\n<p>The <em>weak</em> version of this is that people have perverse incentives to game the metrics. If your metric is \"number of bugs in the bug tracker\", people will start spuriously closing bugs just to get the number down. </p>\n<p>The <em>strong</em> version of the law is that even 100% honest pursuit of a metric, taken far enough, is harmful to your goals, and this is an inescapable consequence of the difference between metrics and values. We have metrics in the first place because what we actually <em>care about</em> is nonquantifiable. There's some <em>thing</em> we want more of, but we have no way of directly measuring that thing. We <em>can</em> measure something that looks like a rough approximation for our goal. But it's <em>not</em> our goal, and if we replace the metric with the goal, we start taking actions that favor the metric over the goal.</p>\n<p>Say we want more reliable software. How do you measure \"reliability\"? You can't. But you <em>can</em> measure the number of bugs in the bug tracker, because fewer open bugs roughly means more reliability. <strong>This is not the same thing</strong>. I've seen bugs fixed in ways that made the system <em>less</em> reliable, but not in ways that translated into tracked bugs.</p>\n<p>I am a firm believer in the strong version of Goodhart's law. Mostly because of this:</p>\n<p><img alt=\"A peacock with its feathers out. The peacock is scremming\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/2573503d-bc57-49ce-aa26-9d399d801118.jpg?w=960&fit=max\"/></p>\n<p>What does a peahen look for in a mate? A male with maximum fitness. What's a metric that approximates fitness? How nice the plumage is, because nicer plumage = more calories energy to waste on plumage.<sup id=\"fnref:peacock\"><a class=\"footnote-ref\" href=\"#fn:peacock\">1</a></sup> But that only <em>approximates</em> fitness, and over generations the plumage itself becomes the point at the cost of overall bird fitness. Sexual selection is Goodhart's law in action.</p>\n<div class=\"subscribe-form\"></div>\n<p>If the blind watchmaker can fall for Goodhart, people can too.</p>\n<h3>Examples in Engineering</h3>\n<p>Goodhart's law is a warning for pointy-haired bosses who up with terrible metrics: lines added, feature points done, etc. I'm more interested in how it affects the metrics we set for ourselves that our bosses might never know about.</p>\n<ul>\n<li>\"Test coverage\" is a proxy for how thoroughly we've tested our software. It diverges when we need to test lots of properties of the same lines of code, or when our worst bugs are emergent at the integration level.</li>\n<li>\"Cyclomatic complexity\" and \"function size\" are proxies for code legibility. They diverges when we think about global module legibility, not local function legibility. Then too many functions can obscure the code and data flow.</li>\n<li>Benchmarks are proxies for performant programs, and diverge when improving benchmarks slows down unbenchmarked operations.</li>\n<li>Amount of time spent pairing/code reviewing/debugging/whatever proxies \"being productive\".</li>\n<li><a href=\"https://dora.dev/\" target=\"_blank\">The DORA report</a> is an interesting case, because it claims four metrics<sup id=\"fnref:metrics\"><a class=\"footnote-ref\" href=\"#fn:metrics\">2</a></sup> are proxies to ineffable goals like \"elite performance\" and <em>employee satisfaction</em>. It also argues that you should minimize commit size to improve the DORA metrics. A proxy of a proxy of a goal!</li>\n</ul>\n<h3>What can we do about this?</h3>\n<p>No, I do not know how to avoid a law that can hijack the process of evolution.</p>\n<p>The 2023 DORA report suggests readers should avoid Goodhart's law and \"assess a team's strength across a wide range of people, processes, and technical capabilities\" (pg 10), which is kind of like saying the fix to production bugs is \"don't write bugs\". It's a guiding principle but not actionable advice that gets to that principle.</p>\n<p>They also say \"to use a combination of metrics to drive deeper understanding\" (ibid), which makes more sense at first. If you have metrics X and Y to approximate goal G, then overoptimizing X <em>might</em> hurt Y, indicating you're getting further from G. In practice I've seen it turn into \"we can't improve X because it'll hurt Y and we can't improve Y because it'll hurt X.\" This <em>could</em> mean we're at the best possible spot for G, but more often it means we're trapped very far from our goal. You could come up with a weighted combination of X and Y, like 0.7X + 0.3Y, but <em>that too</em> is a metric subject to Goodhart. </p>\n<p>I guess the best I can do is say \"use your best engineering judgement\"? Evolution is mindless, people aren't. Again, not an actionable or scalable bit of advice, but as I grow older I keep finding \"use your best judgement\" is all we can do. Knowledge work is ineffable and irreducible.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:peacock\">\n<p>This sent me down a rabbit hole; turns out scientists are still debating what <em>exactly</em> the peacock's tail is used for! Is it sexual selection? Adverse signalling? Something else??? <a class=\"footnote-backref\" href=\"#fnref:peacock\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:metrics\">\n<p>How soon commits get to production, deployment frequency, percent of deployments that cause errors in production, and mean time to recovery. <a class=\"footnote-backref\" href=\"#fnref:metrics\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/goodharts-law-in-software-engineering/", "published": "2024-09-17T16:33:40.000Z", "updated": "2024-09-17T16:33:40.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/why-not-comments/", "title": "Why Not Comments", "description": "<h2>Logic For Programmers v0.3</h2>\n<p><a href=\"https://leanpub.com/logic/\" target=\"_blank\">Now available</a>! It's a light release as I learn more about formatting a nice-looking book. You can see some of the differences between v2 and v3 <a href=\"https://bsky.app/profile/hillelwayne.com/post/3l3egdqnqj62o\" target=\"_blank\">here</a>.</p>\n<h2>Why Not Comments</h2>\n<p>Code is written in a structured machine language, comments are written in an expressive human language. The \"human language\" bit makes comments more expressive and communicative than code. Code has a limited amount of something <em>like</em> human language contained in identifiers. \"Comment the why, not the what\" means to push as much information as possible into identifiers. <a href=\"https://buttondown.com/hillelwayne/archive/3866bd6e-22c3-4098-92ef-4d47ef287ed8\" target=\"_blank\">Not all \"what\" can be embedded like this</a>, but a lot can.</p>\n<p>In recent years I see more people arguing that <em>whys</em> do not belong in comments either, that they can be embedded into <code>LongFunctionNames</code> or the names of test cases. Virtually all \"self-documenting\" codebases add documentation through the addition of identifiers.<sup id=\"fnref:exception\"><a class=\"footnote-ref\" href=\"#fn:exception\">1</a></sup></p>\n<p>So what's something in the range of human expression that <em>cannot</em> be represented with more code?</p>\n<p>Negative information, drawing attention to what's <em>not</em> there. The \"why nots\" of the system.</p>\n<h3>A Recent Example</h3>\n<p>This one comes from <em>Logic for Programmers</em>. For convoluted technical reasons the epub build wasn't translating math notation (<code>\\forall</code>) into symbols (<code>∀</code>). I wrote a script to manually go through and replace tokens in math strings with unicode equivalents. The easiest way to do this is to call <code>string = string.replace(old, new)</code> for each one of the 16 math symbols I need to replace (some math strings have multiple symbols).</p>\n<p>This is incredibly inefficient and I could instead do all 16 replacements in a single pass. But that would be a more complicated solution. So I did the simple way with a comment:</p>\n<div class=\"codehilite\"><pre><span></span><code>Does 16 passes over each string\nBUT there are only 25 math strings in the book so far and most are <5 characters.\nSo it's still fast enough.\n</code></pre></div>\n<p>You can think of this as a \"why I'm using slow code\", but you can also think of it as \"why not fast code\". It's calling attention to something that's <em>not there</em>.</p>\n<h3>Why the comment</h3>\n<p>If the slow code isn't causing any problems, why have a comment at all?</p>\n<div class=\"subscribe-form\"></div>\n<p>Well first of all the code might be a problem later. If a future version of <em>LfP</em> has hundreds of math strings instead of a couple dozen then this build step will bottleneck the whole build. Good to lay a signpost now so I know exactly what to fix later.</p>\n<p>But even if the code is fine forever, the comment still does something important: it shows <em>I'm aware of the tradeoff</em>. Say I come back to my project two years from now, open <code>epub_math_fixer.py</code> and see my terrible slow code. I ask \"why did I write something so terrible?\" Was it inexperience, time crunch, or just a random mistake?</p>\n<p>The negative comment tells me that I <em>knew</em> this was slow code, looked into the alternatives, and decided against optimizing. I don't have to spend a bunch of time reinvestigating only to come to the same conclusion. </p>\n<h2>Why this can't be self-documented</h2>\n<p>When I was first playing with this idea, someone told me that my negative comment isn't necessary, just name the function <code>RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffs</code>. Aside from the issues of being long, not explaining the tradeoffs, and that I'd have to change it everywhere if I ever optimize the code... This would make the code <em>less</em> self-documenting. It doesn't tell you what the function actually <em>does</em>.</p>\n<p>The core problem is that function and variable identifiers can only contain one clause of information. I can't store \"what the function does\" and \"what tradeoffs it makes\" in the same identifier. </p>\n<p>What about replacing the comment with a test. I guess you could make a test that greps for math blocks in the book and fails if there's more than 80? But that's not testing <code>EpubMathFixer</code> directly. There's nothing in the function itself you can hook into. </p>\n<p>That's the fundamental problem with self-documenting negative information. \"Self-documentation\" rides along with written code, and so describes what the code is doing. Negative information is about what the code is <em>not</em> doing. </p>\n<h3>End of newsletter speculation</h3>\n<p>I wonder if you can think of \"why not\" comments as a case of counterfactuals. If so, are \"abstractions of human communication\" impossible to self-document in general? Can you self-document an analogy? Uncertainty? An ethical claim?</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:exception\">\n<p>One interesting exception someone told me: they make code \"more self-documenting\" by turning comments into <em>logging</em>. I encouraged them to write it up as a blog post but so far they haven't. If they ever do I will link it here. <a class=\"footnote-backref\" href=\"#fnref:exception\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/why-not-comments/", "published": "2024-09-10T19:40:29.000Z", "updated": "2024-09-10T19:40:29.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/", "title": "Thoughts on \"The Future of TLA+\"", "description": "<p>Last week Leslie Lamport posted <a href=\"https://lamport.azurewebsites.net/tla/future.pdf\" target=\"_blank\">The Future of TLA+</a>, saying \"the future of TLA+ is in the hands of the TLA+ foundation\". Lamport released TLA+ in 1999 and shepherded its development for the past 25 years. Transferring ownership of TLA+ to the official foundation has been in the works for a while now, and now it's time.</p>\n<p>In the document, Lamport also talks about some of his hopes for how the foundation will think about evolving TLA+. He gives three possible costs of any change:</p>\n<ol>\n<li>Breaking existing specifications</li>\n<li>Complicating the language. \"Simplicity is a major goal of TLA+\". </li>\n<li>Complicating the tooling. \"There are few people contributing to the maintenance of the tools.\"</li>\n</ol>\n<p>With this, Lamport proposes removing several features, like octal numbers, submodules and tuple quantification.<sup id=\"fnref:tuple-quantification\"><a class=\"footnote-ref\" href=\"#fn:tuple-quantification\">1</a></sup></p>\n<p>I would be sad if we lost tuple quantification, less so if we lost octal numbers. But what could be <em>added</em> to make TLA+ better? Features that don't just \"save a few keystrokes\" but significantly prove the expressiveness of our specifications.</p>\n<p>Some disclaimers. First, where I'm coming from: most of my contracts are either \"get beginners started with TLA+\" or \"help a company write an exotic spec at the limit of what TLA+ can do.\" So I'm always thinking about how to teach the language better and how to abuse its most obscure features. Did you know about <a href=\"https://lamport.azurewebsites.net/tla/tla2-guide.pdf\" target=\"_blank\">labeled subexpressions</a>? You can do <em>magic</em> with labeled subexpressions.</p>\n<p>So I both want the skill floor lowered and the skill ceiling raised. This is often a contradictory goal.</p>\n<p>Second, these aren't official proposals or anything. I'm just sharing what I'd like to see, given infinite resources and no concerns about tradeoffs.</p>\n<p>Finally, most of these aren't changes to TLA+. They're for TLC.</p>\n<h3>TLA+ vs TLC</h3>\n<p>TLA+ is the specification language. TLC is the model checker used to check that specs written in that language satisfy certain properties. It's not the only tool; you could use <a href=\"https://proofs.tlapl.us/doc/web/content/Home.html\" target=\"_blank\">TLAPS</a> or <a href=\"https://apalache-mc.org/\" target=\"_blank\">Apalache</a> instead. But TLC is the most mature tool and the only one bundled with the standard distribution.</p>\n<p>This is a critical distinction for two reasons. One, TLC can only model-check a subset of TLA+. There are a set of four \"forbidden operators\" that cannot be used in any capacity (though the lead TLC developer [Markus Kuppe] is working on adding one). Lamport lists them in section 3.3 of his essay.</p>\n<p>Two, TLC makes some decisions about how to resolve some unspecified TLA+ operations. For example, <code>CHOOSE x \\in set: P(x)</code> is undefined if no value in <code>set</code> satisfies <code>P</code> (<a href=\"https://lamport.azurewebsites.net/tla/book-02-08-08.pdf\" target=\"_blank\"><em>Specifying Systems</em></a> pg 73). In TLC, it's an error: </p>\n<blockquote>\n<p>Attempted to compute the value of an expression of form<br/>\nCHOOSE x in S: P, but no element of S satisfied P.</p>\n</blockquote>\n<p>This catches a lot of design bugs where you expected something to exist but it doesn't. But it also makes specifying defaults awkward. What if I want to get the record with a certain id, but if there isn't one, return a default value <code>NoRecord</code>? Then I have to write this:</p>\n<div class=\"codehilite\"><pre><span></span><code>RetrieveRecord(id) ==\n IF \\E r \\in records: r.id = id\n THEN CHOOSE r \\in records: r.id = id\n ELSE NoRecord\n</code></pre></div>\n<p>If the condition is longer, this gets more cumbersome, and there's always a risk of them getting out of sync. </p>\n<p>The upside of TLC being separate from TLA+ is that we can add features to TLC without changing the overall semantics of TLA+. That's what Markus is doing in the <a href=\"https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tla2sany/StandardModules/TLC.tla\" target=\"_blank\">TLC module</a>: things like <code>TLCGet</code> and <code>Assert</code> adds new features to <em>model checking TLA+</em> without adding new syntax to TLA+. It should be possible to make a new operator <code>Try(Op, Fail)</code> that evaluates <code>Op</code> and, if it fails, returns <code>Fail</code> instead. Then we can just do</p>\n<div class=\"codehilite\"><pre><span></span><code>RetrieveRecord(id) ==\n Try(CHOOSE r \\in records: r.id = id, NoRecord)\n</code></pre></div>\n<p>With that in mind, most of the things I want in TLA+ are actually things I want in TLC, with one exception. </p>\n<h2>Things I wished were in TLA+/TLC</h2>\n<h3>Try(Op, Fail)</h3>\n<p>It'd also be nice for equality checking. Right now <code>\"2\" = 2</code> raises an error, even if I want it to be false.</p>\n<p>(Could nested <code>Try</code>s lead to ambiguous behavior? Something to think about.)</p>\n<h3>A more obvious set filtering</h3>\n<p>This is the only change here that would affect TLA+ itself and not just TLC. This is map and filter in TLA+:</p>\n<div class=\"codehilite\"><pre><span></span><code>\\* Map\n{M(x): x \\in set}\n\\* Filter\n{x \\in set: F(x)}\n</code></pre></div>\n<p>Beginners can never remember which is which. Even experienced people often forget which is which! I'd like a different keyword for each, so we could instead write</p>\n<div class=\"codehilite\"><pre><span></span><code>\\* Filter\n{x \\in set WHERE F(x)}\n\\* Mapfilter\n{M(x): x \\in set WHERE F(x)}\n</code></pre></div>\n<p>Actual keyword may vary. </p>\n<h3>A linter</h3>\n<p>In the past few years TLA+ tooling has gotten a <em>lot</em> better. Among other things, there's now a <a href=\"https://github.com/tlaplus/vscode-tlaplus\" target=\"_blank\">debugger</a>, a <a href=\"https://github.com/tlaplus-community/tree-sitter-tlaplus\" target=\"_blank\">treesitter plugin</a>, and an <a href=\"https://will62794.github.io/tla-web/#!/home?specpath=.%2Fspecs%2FTwoPhase.tla\" target=\"_blank\">interactive spec explorer</a>. I hope we soon see a linter, something that could detect TLA+ snippets with valid-but-unexpected behavior. In particular, something that could detect this:</p>\n<div class=\"codehilite\"><pre><span></span><code>/\\ A\n/\\ B\n => C\n</code></pre></div>\n<p>That extra space before <code>=></code> means its parsed as <code>A /\\ (B => C)</code>, not as the expected <code>(A /\\ B) => C</code>. I once lost a day to this. I know at least three other people who also lost a day to this. It is 100% valid TLA+ and we desperately need a tool to tell us when it happens because <em>my gosh</em> is it a morale-killer. </p>\n<div class=\"subscribe-form\"></div>\n<h3>Support for the forbidden operators</h3>\n<p>There are four operators that are unsupported by TLC and virtually unused in any specs: <code>\\cdot</code>, <code>\\EE</code>, <code>\\AA</code>, and <code>-+-></code>. I have no idea how to use <code>\\cdot</code> but Markus is steadily adding support for it. <code>\\AA</code> exists for completeness and even Lamport can't think of a good use for it. <code>-+-></code> could be useful (It's the LTL <a href=\"https://en.wikipedia.org/wiki/Linear_temporal_logic#Weak_until_and_strong_release\" target=\"_blank\">weak release</a>), but nobody's paid it any attention.</p>\n<p><code>\\EE</code> is the interesting one, in that we know <em>exactly</em> how useful it would be. <code>\\EE x: P(x)</code> is the <strong>temporal quantifier</strong> and is defined as \"there exists a sequence of <code>x</code>s that make the temporal formula <code>P(x)</code> true for every step of the behavior. Critically, <code>x</code> can be a <em>different</em> value in each step, whereas <code>\\E x: P(x)</code> would require the <em>same</em> value in each step.</p>\n<p>Why is this so important? One word: <a href=\"https://hillelwayne.com/post/refinement/\" target=\"_blank\">refinement</a>.</p>\n<p>Say we have an abstract server model and we want make a more detailed version of just the <em>server</em>. We can <em>refine</em> it by instantiating the abstract spec like this:</p>\n<div class=\"codehilite\"><pre><span></span><code>Abstract == INSTANCE AbstractServer \n WITH abstract_var1 <- formula1,\n abstract_var2 <- formula2,\n \\* ...\n\nRefinement == Abstract!Spec\n</code></pre></div>\n<p>See the above link for more details. The trouble is that we need to apply substitutions for <em>every</em> variable. If the abstract spec models both the server and the client, our refinement also needs to refine the client state, too! And more frustratingly, if the abstract spec has some kind of bookkeeping variable, say a <a href=\"https://learntla.com/core/functions.html#state-sweeping\" target=\"_blank\">state-sweeper</a> or <a href=\"https://learntla.com/topics/aux-vars.html\" target=\"_blank\">auxiliary variable</a>, you have to refine that, too.</p>\n<p>But TLA+ provides an out here. Using <code>\\EE</code>, we can write the refinement like this:</p>\n<div class=\"codehilite\"><pre><span></span><code>Abstract(aux_var, client_var) == INSTANCE AbstractServer \n WITH abstract_var1 <- formula1,\n abstract_var2 <- formula2,\n \\* ...\n\nRefinement == \\EE a, c: Abstract(a, c)!Spec\n</code></pre></div>\n<p>Lamport calls this <strong>variable hiding</strong> (<em>SS</em> pg 41).</p>\n<p>Now I don't know much about the internals of TLC, but my best guess is that <code>\\EE</code> would be <em>extraordinarily</em> expensive to check. I think a regular refinement you can check as you go, since you can generate the full abstract transition at the same time you generate the main spec's transition. But with variable hiding, you only have information to create <em>part</em> of the abstract transition. I think the only way to do it would be to generate the abstract state graph first and then look for a transition that matches your current transition.</p>\n<p>That is <em>at the very least</em> an NP-Complete problem, one you have to compute for <em>every edge in your refinement graph</em>. But maybe there are some special cases (like aux variables that do not affect behavior) which would be cheaper to check?</p>\n<h3>\\E-stealing</h3>\n<p>This one's both the most niche and probably the hardest to implement, so I don't expect to see it anytime soon. But this is <em>my</em> newsletter and it bothers <em>me</em> specifically.</p>\n<p>In <a href=\"https://hillelwayne.com/post/composing-tla/\" target=\"_blank\">Composing TLA+ Specifications with State Machines</a> I used this trick to compose two specs:</p>\n<div class=\"codehilite\"><pre><span></span><code>Action1 ==\n /\\ state = \"start\"\n /\\ DoThing\n /\\ state' = \"wait\"\n\nSync ==\n LET i == << state, state' >> IN\n CASE i == << \"start\", \"wait\" >> ->\n SomeSyncRules\n [] << \"wait, start\" >> ->\n DifferentSyncRules\n</code></pre></div>\n<p>So far, so good. But what if I take a nondeterministic action?</p>\n<div class=\"codehilite\"><pre><span></span><code>Action2 ==\n /\\ state = \"wait\"\n /\\ \\E x \\in set:\n DoThing(x)\n /\\ state' = \"start\"\n\n\nSync ==\n LET i == << state, state' >> IN\n CASE i == << \"start\", \"wait\" >> ->\n SomeSyncRules\n [] << \"wait, start\" >> ->\n ???\n</code></pre></div>\n<p>There's no way for the body of <code>Sync</code> to <em>know</em> which value <code>Action2</code> picked for <code>x</code>. I've found <code>Sync</code> actions are a great way to compose specifications, and this \"opaqueness\" limits how strong it can be. It'd be great to have some way of side-channeling out what <code>x</code> was picked, without changing the essential semantics of <code>Action2</code>. Preferably without even needing to modify it.</p>\n<p>This might also be useful for <a href=\"https://arxiv.org/abs/2006.00915\" target=\"_blank\">test case generation</a>, since you can augment the output traces with more information. </p>\n<p>I have no idea what this would look like or how it would behave. It's just a pipe dream of mine.</p>\n<h3>Other pipe dreams</h3>\n<p>A better function update syntax. Some way to <a href=\"https://www.hillelwayne.com/post/tla-adt/#dynamic-stacks\" target=\"_blank\">partially refine one spec into multiple</a>, seamlessly upgrading their individual variables into function variables (without <a href=\"https://www.hillelwayne.com/post/tla-adt/#fixing-the-action\" target=\"_blank\">too much boilerplate</a>). <del>Being able to inline a config file in a spec so you don't need two separate files.</del>[^inlining] Being able to call tlc without a config file and passing in flags. Support for <code>-+-></code>, just for giggles. Clock variables. Non-latex ASCII syntax. UNCHANGED (all vars except <code><<a, b>></code>). Parallelized liveness checking. A tree that grants all my wishes.</p>\n<h2>[^inlining]: This is now provisionally possible! You can see an example <a href=\"https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/test-model/Github866.tla\" target=\"_blank\">here</a>, run it with <code>tlc -config Github866.tla Github866.tla</code></h2>\n<h3>Comment Section</h3>\n<p>Buttondown now has comments! I'm experimentally enabling it for now, so you should be able to comment on the <a href=\"\" target=\"_blank\">email's archive page</a>. Again, <em>this is experimental</em>. If I end up having to spend all my time community moderating, comments are going down again. Be nice to each other.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:tuple-quantification\">\n<p><code>CHOOSE << x, y >> \\in set \\X set: x < y</code>. <a class=\"footnote-backref\" href=\"#fnref:tuple-quantification\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/what-could-be-added-to-tla/", "published": "2024-09-04T17:00:19.000Z", "updated": "2024-09-04T17:00:19.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/", "title": "State and time are the same thing", "description": "<p style=\"height:16px; margin:0px !important;\"></p>\n<h2>Time is state</h2>\n<p>Imagine I put an ordinary ticking <a href=\"https://en.wikipedia.org/wiki/Quartz_clock\" target=\"_blank\">quartz clock</a> in an empty room. I walk in, and ten minutes later I walk out with two photograph prints.<sup id=\"fnref:prints\"><a class=\"footnote-ref\" href=\"#fn:prints\">1</a></sup> In the 1st one, the second hand is pointing at the top of the clock, in the 2nd it's pointing at the bottom. Are these two copies of the same photo, or are they two different pictures?</p>\n<p><img alt=\"A quartz clock with minute and second hands\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/a6756666-c6f1-4ac7-9445-5ba6efa1406b.png?w=960&fit=max\"/> </p>\n<p>There's no trick here, the answer is \"different photos\". Since the clock looks different, time must have passed between the two. More formally, we can represent the clock as a <strong>state vector</strong> of <code>(hours, minutes, seconds)</code>. Since the two pictures have different state vectors, they must represent different photos.</p>\n<p>Now I repeat the process and come out with two prints, both showing the same time. Are these two copies of the same photo or two different photos?</p>\n<p>It's <em>possible</em> for the pictures to be different but there's no way to be certain. I could have taken the two photos half a second apart so that time passed but the clock didn't tick yet. There's no observational difference between \"time didn't pass\" and \"time passed but the state vector is the same\". We can model time only passing in one second increments, as any shorter passage of time is not reflected in the state vector.</p>\n<p>Things would be different if you had access to the clock internals. The clock is powered by a quartz crystal that oscillates at approximately 2^15 hz, and a digital circuit inside the clock is counting that number as \"one second\". If you could read the memory inside the clock, then you could distinguish \"00:00:15\" and \"00:00:15 + 2^14 oscillations\".</p>\n<p>But in our current system that state is internal to the watch. Until the circuits turn the internal value into an observable value, we cannot recognize the passage of time for the clock.</p>\n<p>The only way we can see the passage of time is by measuring changes in observable state.</p>\n<div class=\"subscribe-form\"></div>\n<h2>State is time</h2>\n<p>Pseudocode snippet:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"nf\">f</span><span class=\"p\">(</span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"o\">&</span><span class=\"n\">c</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"o\">*</span><span class=\"n\">c</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mi\">2</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p>I'm trying to say that we pass a reference of a variable into <code>f</code>, so <code>int x = 1; f(x); print(x)</code> will output 3. Calling <code>f(x)</code> permanently splits our program into two eras. Before <code>f(x)</code>, all calculations with <code>x</code> will get one thing. <em>Anno Effexi</em>, all calculations with <code>x</code> will get another thing. The update to <code>x</code> advanced time. </p>\n<p>Now a more complicated case:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"nf\">f</span><span class=\"p\">(</span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"o\">&</span><span class=\"n\">c</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"o\">*</span><span class=\"n\">c</span><span class=\"o\">++</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"o\">*</span><span class=\"n\">c</span><span class=\"o\">++</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p>Does this advance time one step or two? Depends on whether the program is single-threaded or concurrent. If it's single threaded, when <code>f</code> executes there's nothing else that can read x in between the two updates, so the first mutation is \"internal\". Externally it looks like there was only one observable mutation <code>x += 2</code>. On the other hand, if the program is concurrent, it's possible for some other thread or w/e to read x in between the two statements of <code>f</code>. That makes the internal mutation observable, so time is advanced twice. There are now three eras of our program with different possible behaviors.</p>\n<p>Changes in state matter in that they create new times.</p>\n<h3>The consequences</h3>\n<p>Some function programmers stay \"shared mutable state is the enemy\". I think it's more like \"time is the enemy\", and time represents itself as mutable state. If a state update is <em>purely internal</em> and <em>cannot</em> affect the observable state (such as a statement in an uncommitted transaction), then it does not advance time.</p>\n<p>I find this a good model to reason about abstract systems. It also motivates formal methods concepts like bisimulation and <a href=\"https://hillelwayne.com/post/refinement/\" target=\"_blank\">refinement</a>, which I really need to do an updated post on.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:prints\">\n<p>I've got a GendankenWorks instant digital camera that takes digital pictures but lets me print them too. I'm sure <em>someone</em> sells something like this. <a class=\"footnote-backref\" href=\"#fnref:prints\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/state-and-time-are-the-same-thing/", "published": "2024-08-27T18:13:37.000Z", "updated": "2024-08-27T18:13:37.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/", "title": "An idea for teaching formal methods better", "description": "<p>I was recently commissioned by a company to make a bespoke <a href=\"https://www.learntla.com\" target=\"_blank\">TLA+</a> workshop with a strong emphasis on reading specifications. I normally emphasize <em>writing</em> specs, so this one will need a different approach.</p>\n<p>While working on it, I had an idea that <em>might</em> make teaching TLA+— and other <a href=\"https://buttondown.com/hillelwayne/archive/a-very-brief-intro-to-formal-methods-aka-my-job/\" target=\"_blank\">formal methods</a>— a little easier.</p>\n<h2>Pseudospecs</h2>\n<p>There are two problems to reading a spec:</p>\n<ol>\n<li>Learning the mental model of what TLA+ is actually <em>doing</em></li>\n<li>Learning how to actually read TLA+.</li>\n</ol>\n<p>The second problem is way, <em>way</em> bigger than it seems, because TLA+ has immense expressive power packed in a scary-looking syntax. Like take this spec:</p>\n<div class=\"codehilite\"><pre><span></span><code>---- MODULE foo ----\nCONSTANT Servers\nVARIABLE counters\n\nInit ==\n counters = [s \\in Servers |-> 0]\n\nZero(s) ==\n /\\ counters[s] > 0\n /\\ counters' = [counters EXCEPT ![s] = 0]\n\nInc(s) ==\n counters' = [counters EXCEPT ![s] = @ + 1]\n\nNext ==\n \\/ \\E s \\in Servers:\n \\/ Zero(s)\n \\/ Inc(s)\n\nSpec == Init /\\ [][Next]_counters\n====\n</code></pre></div>\n<p>What's <code>/\\</code>? What's <code>![s]</code>? What's <code>[][Next]_counters</code>? To understand this spec, I have to <em>understand how to read TLA+</em>. This isn't an insurmountable obstacle, because otherwise <em>nobody</em> would know TLA+, but it could be the difference between \"10 people learn TLA+\" and \"7 people learn TLA+\". </p>\n<p>My idea is to provide the spec along with a pseudospec, which could look like this:</p>\n<div class=\"codehilite\"><pre><span></span><code>Params {servers}\n\nInitialize with {\n counters: Map(s in Servers: 0)\n}\n\naction Zero(s) {\n requires {\n counters[s] > 0\n }\n counters[s] := 0\n}\n\naction Inc(s) {\n counters[s] +:= 1\n}\n\naction Next {\n pick some s in Servers and { \n either do Zero(s)\n or do Inc(s)\n }\n</code></pre></div>\n<p>(Still working on what to do about the <code>Spec</code> operator.)</p>\n<p>Couple things to notice. One: it's not a consistent translation between two languages, but between language and meaning. There's no formal grammar for the pseudospec. I need inconsistency for reasons I'll talk about later. </p>\n<p>Two: the translation is not purely local. Things get moved around a little bit. <code>Zero</code> being an action affects how I translate <code>\\E</code>.</p>\n<p>Three: the translation is kind of <a href=\"https://en.wikipedia.org/wiki/Metaphrase\" target=\"_blank\">metaphrasic</a>. It's close to, but not exactly, a line-by-line translation. A person who has both files open in a split can correlate the spec and pseudospec.<sup id=\"fnref:pluscal\"><a class=\"footnote-ref\" href=\"#fn:pluscal\">1</a></sup></p>\n<p>Most directly, this will help people understand what the spec is doing, which gives me a basis to teach model-checking, safety properties, etc. But I think this will also makes learning the TLA+ syntax easier, by acting like an answer sheet. The student can read the TLA+ and, if they get stuck, look at the pseudospec for meaning. </p>\n<p>My moonshot hope is that this also helps with writing specs, by giving people a clearer sense of the standard TLA+ idioms. I can translate the same syntax slightly differently depending on the way it's used, and whether it's a general construct or one for just the spec. That might be doing too much with this concept, though. </p>\n<h2>Problems I see</h2>\n<div class=\"subscribe-form\"></div>\n<p>First is that there's lots of nuances that would be lost in the translation. In writing</p>\n<div class=\"codehilite\"><pre><span></span><code>Init ==\n /\\ x \\in 1..5\n\n-->\n\nInitialize with {\n x: pick some in 1..5\n}\n</code></pre></div>\n<p>I lose the idea that <code>Init</code> <em>isn't special</em>, it's just a boolean operator like anything else. The student will have to unlearn that misconception if they ever need to do tricks like <a href=\"https://learntla.com/topics/optimization.html#ignore-part-of-the-state-space\" target=\"_blank\">FastInit</a>. </p>\n<p>This is an essential tradeoff in pedagogy: is it better to teach the full picture now or the easy thing first and fix the misconception later? The teacher side of me knows that the white lie is better in the long run, but the expert side of me <em>hates</em> this. </p>\n<p>Second, not every TLA+ symbol has a 1-1 mapping with English. I did the translation</p>\n<div class=\"codehilite\"><pre><span></span><code>\\E s \\in Servers:\n \\/ Zero(s)\n \\/ Inc(s)\n\n-->\n\npick some s in Servers and { \n either do Zero(s)\n or do Inc(s)\n}\n</code></pre></div>\n<p>That's because <code>Zero</code> and <code>Inc</code> are actions— things that update state. If they were regular operators, If P and Q are regular operators, I'd translate it like this:</p>\n<div class=\"codehilite\"><pre><span></span><code>exists an s in S {\n P(s) || Q(s)\n}\n</code></pre></div>\n<p>Depending on the context, I'll translate <code>\\E</code> in two different ways.</p>\n<p>This ties back to problem #1. In TLA+, <em>these are the same thing</em>. Performing an action is <em>literally</em> saying that it's a true description of the next-state relation. This has the consequence of <em>looking</em> like it updates the state relation, but that's only in certain (exceptionally common) circumstances. This is all incredibly elegant and intuitive to a TLA+ expert, but not for a beginner. So I have to introduce some inconsistencies. </p>\n<p>Problem #3 is just how much syntax I'll need to translate. What do I do for <code>[A -> B]</code>, <code>WF_vars</code>, or <code>\\AA</code>? I can try to avoid them for the early specs but can't do that forever. </p>\n<p>Overall, though, I think the potential benefit of easier learning will outweigh the drawbacks.</p>\n<h3>Will this work?</h3>\n<p>We'll find out!</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:pluscal\">\n<p>Experienced TLA+ users might notice that this is inspired by both PlusCal and <a href=\"https://github.com/informalsystems/quint\" target=\"_blank\">Quint</a>. I can't just teach those because the goal is to get people use TLA+ proper. (Also we really need a word for \"experienced TLA+ user\", like \"Pythonista\" or \"Rustacean\") <a class=\"footnote-backref\" href=\"#fnref:pluscal\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/an-idea-for-teaching-formal-methods-better/", "published": "2024-08-21T16:05:27.000Z", "updated": "2024-08-21T16:05:27.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/texttools-dot-py/", "title": "Texttools dot py", "description": "<p>I make a lot of personal software tools. One of these is \"texttools.py\", which is easiest to explain with an image:</p>\n<p><img alt=\"A GUI with the first three lines of this newsletter in the top, and the bottom has the three lines with markdown quote prefixes\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/ddf06d0f-d8b9-4ae4-8c10-b275e104731e.png?w=960&fit=max\"/> </p>\n<p>Paste text in the top box, choose a transform, output appears in the bottom box. I can already do most of these transformations in vim, or with one of the many online tools out there, but I prefer my script for two reasons:</p>\n<ol>\n<li>There's no context switching. I don't have to leave my current tab or vim buffer and worry about cleanup later. One hotkey opens the GUI, <code><esc></code> closes it, with no break in my workflow.</li>\n<li>It loads in <10 ms.</li>\n</ol>\n<p>It took me like an hour to make and I use it all the time. And it's small enough that I just share the whole script here.</p>\n<h2>How it works</h2>\n<p>Texttools is a python script running a <a href=\"https://docs.python.org/3/library/tkinter.html\" target=\"_blank\">tkinter</a> GUI. I used tkinter because it's a builtin; I would generally <em>not</em> recommend it if you have any better options. On the plus side, being a builtin means you don't need to install a package to use this yourself.</p>\n<div class=\"subscribe-form\"></div>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kn\">import</span> <span class=\"nn\">tkinter</span> <span class=\"k\">as</span> <span class=\"nn\">tk</span>\n<span class=\"kn\">from</span> <span class=\"nn\">tkinter</span> <span class=\"kn\">import</span> <span class=\"n\">N</span><span class=\"p\">,</span> <span class=\"n\">S</span><span class=\"p\">,</span> <span class=\"n\">E</span><span class=\"p\">,</span> <span class=\"n\">W</span><span class=\"p\">,</span> <span class=\"n\">ttk</span>\n\n\n<span class=\"c1\"># Complex transforms go here,</span>\n<span class=\"c1\"># Simple transforms are just lambdas</span>\n<span class=\"k\">def</span> <span class=\"nf\">_wordcount</span><span class=\"p\">(</span><span class=\"n\">s</span><span class=\"p\">:</span> <span class=\"nb\">str</span><span class=\"p\">):</span>\n <span class=\"s2\">\"Returns a tuple of linecount, wordcount, charcount\"</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">s</span><span class=\"o\">.</span><span class=\"n\">splitlines</span><span class=\"p\">()),</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">s</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">()),</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">s</span><span class=\"p\">))</span>\n\n<span class=\"n\">transforms</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n <span class=\"c1\"># Transforms go here, for example</span>\n <span class=\"p\">{</span><span class=\"s2\">\"name\"</span><span class=\"p\">:</span> <span class=\"s2\">\"One line\"</span><span class=\"p\">,</span> <span class=\"s2\">\"transform\"</span><span class=\"p\">:</span> <span class=\"k\">lambda</span> <span class=\"n\">x</span><span class=\"p\">:</span> <span class=\"s2\">\" \"</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"o\">.</span><span class=\"n\">splitlines</span><span class=\"p\">())}</span>\n <span class=\"p\">,{</span><span class=\"s2\">\"name\"</span><span class=\"p\">:</span> <span class=\"s2\">\"Line/Word/Char\"</span><span class=\"p\">,</span> <span class=\"s2\">\"transform\"</span><span class=\"p\">:</span> <span class=\"n\">_wordcount</span><span class=\"p\">}</span>\n <span class=\"p\">]</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">GUI</span><span class=\"p\">():</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">)</span> <span class=\"o\">-></span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span> <span class=\"o\">=</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">Tk</span><span class=\"p\">()</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">active_transform</span> <span class=\"o\">=</span> <span class=\"k\">lambda</span> <span class=\"n\">x</span><span class=\"p\">:</span> <span class=\"n\">x</span> <span class=\"c1\"># start with no transform</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">layout_gui</span><span class=\"p\">()</span>\n\n\n <span class=\"k\">def</span> <span class=\"nf\">layout_gui</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">)</span> <span class=\"o\">-></span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">mainframe</span> <span class=\"o\">=</span> <span class=\"n\">ttk</span><span class=\"o\">.</span><span class=\"n\">Frame</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"p\">,</span> <span class=\"n\">padding</span><span class=\"o\">=</span><span class=\"s2\">\"3 3 12 12\"</span><span class=\"p\">)</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">title</span><span class=\"p\">(</span><span class=\"s2\">\"Text Tools\"</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">mainframe</span><span class=\"o\">.</span><span class=\"n\">grid</span><span class=\"p\">(</span><span class=\"n\">column</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"n\">row</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"n\">sticky</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">N</span><span class=\"p\">,</span> <span class=\"n\">W</span><span class=\"p\">,</span> <span class=\"n\">E</span><span class=\"p\">,</span> <span class=\"n\">S</span><span class=\"p\">))</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">columnconfigure</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"n\">weight</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">rowconfigure</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"n\">weight</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span> <span class=\"o\">=</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">Text</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">mainframe</span><span class=\"p\">,</span> <span class=\"n\">undo</span><span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span><span class=\"o\">.</span><span class=\"n\">grid</span><span class=\"p\">(</span><span class=\"n\">column</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"n\">row</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">)</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">output_box</span> <span class=\"o\">=</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">Text</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">mainframe</span><span class=\"p\">,</span> <span class=\"n\">undo</span><span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">output_box</span><span class=\"o\">.</span><span class=\"n\">grid</span><span class=\"p\">(</span><span class=\"n\">column</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"n\">row</span><span class=\"o\">=</span><span class=\"mi\">3</span><span class=\"p\">)</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span> <span class=\"o\">=</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">Listbox</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">mainframe</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">grid</span><span class=\"p\">(</span><span class=\"n\">column</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"n\">row</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"n\">rowspan</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">t</span> <span class=\"ow\">in</span> <span class=\"n\">transforms</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">insert</span><span class=\"p\">(</span><span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">END</span><span class=\"p\">,</span> <span class=\"n\">t</span><span class=\"p\">[</span><span class=\"s2\">\"name\"</span><span class=\"p\">])</span>\n\n <span class=\"c1\"># Keyboard bindings</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"s2\">\"<Escape>\"</span><span class=\"p\">,</span> <span class=\"k\">lambda</span> <span class=\"n\">_</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">quit</span><span class=\"p\">())</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span><span class=\"o\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"s2\">\"<Tab>\"</span><span class=\"p\">,</span> <span class=\"k\">lambda</span> <span class=\"n\">_</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">focus</span><span class=\"p\">())</span>\n\n <span class=\"c1\"># vvv makes clicking or pressing enter change the transform</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"s2\">\"<Button-1>\"</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">select_transform</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"s2\">\"<Return>\"</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">select_transform</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># vvv makes anything typed in update the output</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span><span class=\"o\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"s2\">\"<Key>\"</span><span class=\"p\">,</span> \n <span class=\"k\">lambda</span> <span class=\"n\">_</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">after</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">update</span><span class=\"p\">))</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span><span class=\"o\">.</span><span class=\"n\">focus</span><span class=\"p\">()</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">update</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">content</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">content_box</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">'1.0'</span><span class=\"p\">,</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">END</span><span class=\"p\">)</span>\n <span class=\"n\">content</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">active_transform</span><span class=\"p\">(</span><span class=\"n\">content</span><span class=\"p\">)</span> \n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">output_box</span><span class=\"o\">.</span><span class=\"n\">delete</span><span class=\"p\">(</span><span class=\"s1\">'1.0'</span><span class=\"p\">,</span> <span class=\"n\">tk</span><span class=\"o\">.</span><span class=\"n\">END</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">output_box</span><span class=\"o\">.</span><span class=\"n\">insert</span><span class=\"p\">(</span><span class=\"s1\">'1.0'</span><span class=\"p\">,</span> <span class=\"n\">content</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">select_transform</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">_</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">selection</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">transform_box</span><span class=\"o\">.</span><span class=\"n\">curselection</span><span class=\"p\">()[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">active_transform</span> <span class=\"o\">=</span> <span class=\"n\">transforms</span><span class=\"p\">[</span><span class=\"n\">selection</span><span class=\"p\">][</span><span class=\"s2\">\"transform\"</span><span class=\"p\">]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">update</span><span class=\"p\">()</span>\n <span class=\"k\">except</span> <span class=\"p\">(</span><span class=\"ne\">ValueError</span><span class=\"p\">,</span> <span class=\"ne\">IndexError</span><span class=\"p\">):</span>\n <span class=\"k\">pass</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">main</span><span class=\"p\">():</span> <span class=\"c1\"># Entry point for pyproject.toml</span>\n <span class=\"n\">gui</span> <span class=\"o\">=</span> <span class=\"n\">GUI</span><span class=\"p\">()</span>\n <span class=\"n\">gui</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">mainloop</span><span class=\"p\">()</span>\n\n<span class=\"k\">if</span> <span class=\"vm\">__name__</span> <span class=\"o\">==</span> <span class=\"s2\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"n\">main</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>Man I forget how much I dislike tkinter until I have to look at it again. If you want to add your own text tools, just put a new item in the global <code>transforms</code> array.</p>\n<p>To make it easier to run the script, I put it in a \"toolkit\" repo with this <a href=\"https://packaging.python.org/en/latest/guides/writing-pyproject-toml/\" target=\"_blank\">pyproject.toml</a>:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"k\">[project]</span>\n<span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">\"toolkit\"</span>\n<span class=\"n\">version</span><span class=\"o\">=</span><span class=\"s2\">\"0.0.1\"</span>\n<span class=\"n\">requires-python</span><span class=\"o\">=</span><span class=\"s2\">\">=3.8\"</span>\n\n<span class=\"k\">[project.gui-scripts]</span>\n<span class=\"n\">toolkit-texttools</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"s2\">\"toolkit.texttools:main\"</span>\n</code></pre></div>\n<p>Then running <code>pip install -e .</code> creates a <code>toolkit-texttools</code> binary (or in my case a <code>.exe</code>).</p>\n<p>Finally, I wrote an <a href=\"https://www.hillelwayne.com/post/ahk-scripts-project/\" target=\"_blank\">AutoHotKey script</a> so I could load it with a keyboard shortcut:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c1\">; & numpagedown + t both pressed at same time</span>\n<span class=\"n\">NumpadPgDn</span> <span class=\"o\">&</span> <span class=\"n\">t</span><span class=\"o\">::</span> <span class=\"n\">toggle_app</span><span class=\"p\">(</span><span class=\"s\">\"Text Tools\"</span><span class=\"p\">,</span> <span class=\"s\">\"toolkit-texttools.exe\"</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>I like mapping stuff to the numpad because it's guaranteed to not interfere with any OS or program-specific hotkeys.</p>\n<p>Short newsletter this week because I'm still recovering from jetlag. See you all next week!</p>", "url": "https://buttondown.com/hillelwayne/archive/texttools-dot-py/", "published": "2024-08-14T17:19:46.000Z", "updated": "2024-08-14T17:19:46.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/", "title": "Why I prefer rST to markdown", "description": "<p>I just published a new version of <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Logic for Programmers</a>! v0.2 has epub support, content on constraint solving and formal specification, and more! Get it <a href=\"https://leanpub.com/logic/\" target=\"_blank\">here</a>.</p>\n<p>This is my second book written with <a href=\"https://www.sphinx-doc.org/en/master/\" target=\"_blank\">Sphinx</a>, after the new <a href=\"https://www.learntla.com/\" target=\"_blank\">Learn TLA+</a>. Sphinx uses a peculiar markup called <a href=\"https://docutils.sourceforge.io/rst.html\" target=\"_blank\">reStructured Text</a> (rST), which has a steeper learning curve than markdown. I only switched to it <em>after</em> writing a couple of books in markdown and deciding I needed something better. So I want to talk about why rst was that something.<sup id=\"fnref:rst\"><a class=\"footnote-ref\" href=\"#fn:rst\">1</a></sup></p>\n<h2>Why rst is better</h2>\n<p>The most important difference between rst and markdown is that markdown is a lightweight representation of html, while rst is a midweight representation of an abstract documentation tree.</p>\n<p>It's easiest to see this with a comparison. Here's how to make an image in markdown:</p>\n<div class=\"codehilite\"><pre><span></span><code>\n</code></pre></div>\n<p>Technically, you don't even need a parser for this. You just need a regex to transform it into <code><img alt=\"alttext\" src=\"example.jpg\"/></code>. Most modern markdown engines <em>do</em> parse this into an intermediate representation, but the <em>essence</em> of markdown is that it's a lightweight html notation.</p>\n<p>Now here's how to make an image in rst:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"p\">..</span> <span class=\"ow\">image</span><span class=\"p\">::</span> example.jpg\n <span class=\"nc\">:alt:</span> alttext\n</code></pre></div>\n<p><code>.. image::</code> defines the image \"directive\". When Sphinx reads it, it looks up the registered handler for the directive, finds <code>ImageDirective</code>, invokes <code>ImageDirective.run</code>, which returns an <code>image_node</code>, which is an object with an <code>alt</code> field containing \"alttext\". Once Sphinx's processed all nodes, it passes the whole doctree to the HTML Writer, which looks up the rendering function for <code>image_node</code>, which tells it to output an <code><image></code> tag.</p>\n<p>Whew that's a mouthful. And for all that implementation complexity, we get… an interface that has 3x the boilerplate as markdown.</p>\n<p>On the other hand, the markdown image is hardcoded as a special case in the parser, while the rst image is not. It was added in the exact same way as every other directive in rst: register a handler for the directive, have the handler output a specific kind of node, and then register a renderer for that node for each builder you want.</p>\n<p>This means you can extend Sphinx with new text objects! Say you that instead of an <code><image></code>, you want a <code><figure></code> with a <code><figcaption></code>. In basic markdown you have to manually insert the html, with Sphinx you can just register a new <code>figure</code> directive. You can even make your <code>FigureDirective</code> subclass <code>ImageDirective</code> and have it do most of the heavy lifting.</p>\n<p>The second benefit is more subtle: you can transform the doctree before rendering it. This is how Sphinx handles cross-referencing: if I put a <code>foo</code> anchor in one document and <code>:ref:`image <foo>`</code> in another, Sphinx will insert the right URL during postprocessing. The transformation code is also first-class with the rest of the build process: I can configure a transform to only apply when I'm outputting html, have it trigger in a certain stage of building, or even remove a builtin transform I don't want to run.</p>\n<p>Now, most people may not need this kind of power! Markdown is ubiquitous because it's lightweight and portable, and rst is anything but. But <em>I</em> need that power.</p>\n<div class=\"subscribe-form\"></div>\n<h3>One use case</h3>\n<p><em>Logic for Programmers</em> is a math-adjacent book, and all good math books need exercises for the reader. It's easier to write an exercise if I can put it and the solution right next to each other in the document. But for readers, I want the solutions to show up in the back of the book. I also want to link the two together, and since I might want to eventually print the book, the pdfs should also include page references. Plus they need to be rendered in different ways for latex (pdf) output and epub output. Overall lots of moving parts.</p>\n<p>To handle this I wrote my own exercise extension.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"c\">.. in chapter.rst</span>\n<span class=\"p\">..</span> <span class=\"ow\">exercise</span><span class=\"p\">::</span> Fizzbuzz\n <span class=\"nc\">:name:</span> ex-fizzbuzz\n\n An exercise\n\n<span class=\"p\">..</span> <span class=\"ow\">solution</span><span class=\"p\">::</span> ex-fizzbuzz\n\n A solution\n\n<span class=\"c\">.. in answers.rst</span>\n\n<span class=\"p\">..</span> <span class=\"ow\">solutionlist</span><span class=\"p\">::</span>\n</code></pre></div>\n<p>How these nodes are processed depends on my compilation target. I like to debug in HTML, so for HTML it just renders the exercise and solution inline.</p>\n<p>When generating epub and latex, though, things works a little differently. After generating the whole doctree, I run a transform that moves every solution node from its original location to under <code>solutionlist</code>. Then it attaches a reference node to every exercise, linking it to the <em>new</em> solution location, and vice versa. So it starts like this (using Sphinx's \"pseudoxml\" format): </p>\n<div class=\"codehilite\"><pre><span></span><code>--<span class=\"w\"> </span>chapter.rst\n<span class=\"nt\"><exercise_node</span><span class=\"w\"> </span><span class=\"na\">ids=</span><span class=\"s\">\"ex-fizzbuzz\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span><span class=\"nt\"><title></span>\n<span class=\"w\"> </span>Fizzbuzz\n<span class=\"w\"> </span><span class=\"nt\"><paragraph></span>\n<span class=\"w\"> </span>An<span class=\"w\"> </span>exercise\n<span class=\"nt\"><solution_node</span><span class=\"w\"> </span><span class=\"na\">ids=</span><span class=\"s\">\"ex-fizzbuzz-sol\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span><span class=\"nt\"><paragraph></span>\n<span class=\"w\"> </span>A<span class=\"w\"> </span>solution\n\n--<span class=\"w\"> </span>answers.rst\n<span class=\"nt\"><solutionlist_node></span>\n</code></pre></div>\n<p>And it becomes this:</p>\n<div class=\"codehilite\"><pre><span></span><code>--<span class=\"w\"> </span>chapter.rst\n<span class=\"nt\"><exercise_node</span><span class=\"w\"> </span><span class=\"na\">ids=</span><span class=\"s\">\"ex-fizzbuzz\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span><span class=\"nt\"><title></span>\n<span class=\"w\"> </span>Fizzbuzz\n<span class=\"w\"> </span><span class=\"nt\"><paragraph></span>\n<span class=\"w\"> </span>An<span class=\"w\"> </span>exercise\n<span class=\"w\"> </span><span class=\"nt\"><exsol_ref_node</span><span class=\"w\"> </span><span class=\"na\">refuri=</span><span class=\"s\">\"/path/to/answers#ex-fizzbuzz-sol\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span>Solution\n\n--<span class=\"w\"> </span>answers.rst\n<span class=\"nt\"><solutionlist_node></span>\n<span class=\"w\"> </span><span class=\"nt\"><solution_node</span><span class=\"w\"> </span><span class=\"na\">ids=</span><span class=\"s\">\"ex-fizzbuzz-sol\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span><span class=\"nt\"><paragraph></span>\n<span class=\"w\"> </span>A<span class=\"w\"> </span>solution\n<span class=\"w\"> </span><span class=\"nt\"><exsol_ref_node</span><span class=\"w\"> </span><span class=\"na\">refuri=</span><span class=\"s\">\"/path/to/chapter#ex-fizzbuzz\"</span><span class=\"nt\">></span>\n<span class=\"w\"> </span>(back)\n</code></pre></div>\n<p style=\"height:16px; margin:0px !important;\"></p>\n<p>The Latex builder renders this by wrapping each exercise and solution in an <a href=\"https://ctan.org/pkg/exercise\" target=\"_blank\">answers environment</a>, while the epub builder renders the solution as a <a href=\"https://help.apple.com/itc/booksassetguide/en.lproj/itccf8ecf5c8.html\" target=\"_blank\">popup footnote</a>.<sup id=\"fnref:exsol_ref\"><a class=\"footnote-ref\" href=\"#fn:exsol_ref\">2</a></sup> Making this work:</p>\n<p><img alt=\"An example of solution popups on an epub reader\" class=\"newsletter-image\" src=\"https://assets.buttondown.email/images/8a7d66e3-56bd-4b7a-95ac-d4fdf88047c7.png?w=960&fit=max\"/> </p>\n<p>It's a complex dance of operations, but it works enormously well. It even helps with creating a \"free sample\" subset of the book: the back of the free sample only includes the solutions from the included subset, not the whole book!</p>\n<h3>\"But I hate the syntax\"</h3>\n<p>When I gush about rST to other programmers, this is the objection I hear the most: it's ugly. </p>\n<p>To which I say, are you really going to avoid using a good tool just because it makes you puke? Because looking at it makes your stomach churn? Because it offends every fiber of your being?</p>\n<p>...Okay yeah that's actually a pretty good reason not to use it. I can't get into lisps for the same reason. I'm not going to begrudge anybody who avoids a tool because it's ugly.</p>\n<p>Maybe you'd find <a href=\"https://github.com/asciidoctor/asciidoctor\" target=\"_blank\">asciidoc</a> more aesthetically pleasing? Or <a href=\"https://mystmd.org/spec\" target=\"_blank\">MyST</a>? Or <a href=\"https://github.com/typst/typst\" target=\"_blank\">Typst</a>? Or <a href=\"https://docs.racket-lang.org/pollen/\" target=\"_blank\">Pollen</a>? Or even <a href=\"https://pandoc.org/MANUAL.html#extension-attributes\" target=\"_blank\">pandoc-extended markdown</a>? There are lots of solid document builders out there! My point isn't that sphinx/rst is exceptionally <em>good</em> for largescale documentation, it's that simple markdown is exceptionally <em>bad</em>. It doesn't have a uniform extension syntax or native support for pre-render transforms.</p>\n<p>This is why a lot of markdown-based documentation generators kind of hack on their own preprocessing step to support new use-cases, which works for the most part (unless you're trying to do something really crazy). But they have to work around the markdown, not in it, which limits how powerful they can be. It also means that most programmer tooling can't understand it well. There's LSP and treesitter for markdown and rst but not for gitbook-markdown or md-markdown or leanpub-markdown.<sup id=\"fnref:treesitter\"><a class=\"footnote-ref\" href=\"#fn:treesitter\">3</a></sup></p>\n<p>But if you find a builder that uses markdown and satisfies your needs, more power to you! I just want to expose people to the idea that doc builders can be a lot more powerful than they might otherwise expect.</p>\n<hr/>\n<h3>No newsletter next week</h3>\n<p>I'll be in Hong Kong.</p>\n<h2>Update 2024-07-31</h2>\n<p>Okay since this is blowing up online I'm going to throw in a quick explanation of <em>Logic for Programmers</em> for all of the non-regulars here. I'm working on a book about how formal logic is useful in day-to-day software engineering. It starts with a basic rundown of the math and then goes into eight different applications, such as property testing, database constraints, and decision tables. It's still in the alpha stages but already 20k words and has a lot of useful content. You can find it <a href=\"https://leanpub.com/logic\" target=\"_blank\">here</a>. Reader feedback highly appreciated!</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:rst\">\n<p>rst is actually independent of Sphinx, but everybody I know who writes rst writes it <em>because</em> they're using Sphinx, so I'll use the two interchangeably. Also typing rST is annoying so I'm typing rst. <a class=\"footnote-backref\" href=\"#fnref:rst\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:exsol_ref\">\n<p>This is why I attach <code>exsol_ref_nodes</code> and not default <code>reference_nodes</code>. Sphinx's epub translator uses an attribute passlist I need to workaround in post-rendering. <a class=\"footnote-backref\" href=\"#fnref:exsol_ref\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n<li id=\"fn:treesitter\">\n<p>This is also one place where rst's ugly syntax works in its favor. I've got a treesitter query that changes the body of todo directives and <em>only</em> todo directives, which is only possible because the rst syntax tree is much richer than the markdown syntax tree. <a class=\"footnote-backref\" href=\"#fnref:treesitter\" title=\"Jump back to footnote 3 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/why-i-prefer-rst-to-markdown/", "published": "2024-07-31T15:34:39.000Z", "updated": "2024-07-31T15:34:39.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/", "title": "My patented Miracle Tonic would have prevented the CrowdStrike meltdown", "description": "<p>Last Friday CrowdStrike did something really bad and it destroyed every airport in the world. I didn't bother to learn anything else about it because I was too busy writing my 10k whitepaper about how all the problems were all caused by one simple mistake: not drinking my patented Miracle Tonic™®.</p>\n<p>Developers who drink my Miracle Tonic write code 100% faster with 100% fewer bugs. This would have prevented the CrowdStrike outage, the 2016 DNC hack, Ariane 5, Therac-25, and that one moth caught in the Harvard Mark II. Developers are also happier at work, suffer no burnout, and keep all standups to <em>exactly</em> five minutes.</p>\n<p>The Miracle Tonic is so effective that it should be <em>immoral</em> not to drink it. It's like if surgeons didn't wash their hands. If you write code for a living and you don't drink my Miracle Tonic, do us all a favor and never touch a computer again. You idiot. You absolute moron. I can't believe you call yourself a \"professional\".</p>\n<p><strong>Frequently Asked Questions:</strong></p>\n<blockquote>\n<p>Do you have any actual evidence that Miracle Tonic actually helps programmers?</p>\n</blockquote>\n<p>Yes I do! All sorts of studies prove the effectiveness of Miracle Tonic:</p>\n<ol>\n<li>One survey found that 68% of devs drinking miracle tonic say their code is \"awesome\". That means it works!</li>\n<li>A <em>double-blind clinical trial</em> found that 7 undergraduates who were given Miracle Tonic wrote 10% better code (using the Hills-Bourne-Davidio Corrected Dolorimetry metric) than the control group (6 undergraduates who were punched in the face).</li>\n<li>Someone found twelve projects on GitHub that didn't use Miracle Tonic and they had 268% worse failure rates than this one project I did with it. That's a P value of 0.00004!</li>\n</ol>\n<p>That's so many studies! I can say with 100% confidence that Miracle Tonic is proven to work beyond a shadow of a doubt.</p>\n<blockquote>\n<p>I read a study saying that Miracle Tonic gives people headaches.</p>\n</blockquote>\n<p>Why are you trusting <em>studies</em>? Are you really gonna listen to some graduate student dweeb who's never been a <em>real programmer</em>?! If you're curious about Miracle Tonic, just try it and see for yourself.</p>\n<blockquote>\n<p>Are there any downsides to drinking Miracle Tonic?</p>\n</blockquote>\n<p>Of course, there is no silver bullet, everything is tradeoffs, etc etc etc. The downside of Miracle Tonic is it doesn't work if your company is a toxic feature factory that cares more about micromanaging mindless worker drones than cultivating good engineers. And don't drink it if you're allergic to peanuts.</p>\n<blockquote>\n<p>This tastes revolting.</p>\n</blockquote>\n<p>I've trained five Miracle Tonic Brand Ambassadors and they all tell me it's delicious. Your taste buds must be wrong.</p>\n<blockquote>\n<p>I tried drinking your Miracle Tonic and it didn't make me a better programmer.</p>\n</blockquote>\n<p><em>How dare you</em>. How dare you spread FUD about the most important programming technology ever made. Were you drinking exactly 12.063 mL at 67° C every 75 minutes? Yeah, thought not. Of course it's not going to work if you didn't follow it properly. And you'd know this if you took my $1500 three-day \"how do drink Miracle Tonic\" course. Which you didn't. <em>Get out of my sight.</em></p>\n<blockquote>\n<p>How does Miracle Tonic compare to competing products, like toad oil, Pirelli's Miracle Elixir, or Design-by-Contract?</p>\n</blockquote>\n<p>Fucking charlatans, all of them.</p>\n<hr/>\n<p>This is the part of my job I dread.</p>\n<p>I do formal methods for a living. Last year 100% of my income came from either writing specifications or training others how to write specifications. This year, due to a lot of work in diversifying my revenue streams, it will only be 90% of my income. This creates an immense pressure to become an ambulance chaser, to see ten billion dollars in economic damage as a chance to say \"look at me!\", a longshot chance to find the next source who'll pay my next month's rent.</p>\n<p>I'm also a True Believer. Of course formal methods would have prevented the CrowdStrike outage! It also would have prevented COVID-19, the Cuban Missile Crisis and the Lincoln assassination. Years of being an advocate have shaped my worldview to <em>always</em> see how it could have helped, how everything is just the right shape nail for the hammer I'm selling you.</p>\n<p>None of this depends on anything particular to formal methods, which is why advocates of every stripe are telling us \"what they should have done\", because every advocate is a true believer, and every advocate wants to get paid. I understand this all, and I get why people do this, but I hate feeling this way, that all this misery begets opportunity. I hate the pressure to say \"they needed X\" as fast as possible, before people lose interest, regardless of whether X would help or even if they were <em>already using</em> X. I hate the feeling that capitalizing on this might compromise my principles.</p>\n<p>Most of all though, I fear the slippery slope from advocating to grifting. There are people out there who saw the outage and got <em>excited</em>. Please, God, let me never be that.</p>", "url": "https://buttondown.com/hillelwayne/archive/my-patented-miracle-tonic-would-have-prevented/", "published": "2024-07-23T15:22:21.000Z", "updated": "2024-07-23T15:22:21.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/", "title": "Keep perfecting your config", "description": "<p>First of all, I wanted to extend a huge and heartfelt thank you to all of the people who bought <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Logic for Programmers</a>. Seeing the interest is incredible motivation to continue improving it. If you read it and have feedback, please share it with me!</p>\n<p>Second, I have a new blogpost out: <a href=\"https://www.hillelwayne.com/post/toolbox-languages/\" target=\"_blank\">Toolbox Languages</a>. It's about five obscure languages I use that make certain hard problems I have much easier. I hope it encourages other people to talk about their toolbox languages (or share new ones with me!). Patreon blog notes (and the cut sixth language) <a href=\"https://www.patreon.com/posts/108180681\" target=\"_blank\">here</a>.</p>\n<p>Third, actual newsletter. </p>\n<h1>Keep perfecting your config</h1>\n<p>Last week I read the article <a href=\"https://arkadiuszchmura.com/posts/stop-perfecting-your-config/\" target=\"_blank\">stop perfecting your config</a>, which argues that people spend too much time tinkering with \"editors, IDEs, terminals [and] other applications\". I firmly believe the opposite, that people should spend <em>more</em> time tinkering. To be fair, the OP says</p>\n<blockquote>\n<p>Before I continue, let’s make one thing clear. By no means am I saying that there is something wrong with trying to master your tools or attempting to adjust them to suit your specific needs. It’s actually the opposite.</p>\n</blockquote>\n<p>So I read this more as \"don't make my mistakes\" than \"don't do what I don't like.\"<sup id=\"fnref:caveat\"><a class=\"footnote-ref\" href=\"#fn:caveat\">1</a></sup> At the same time, it's notable are \"his mistakes\"— what he counted as perfecting his configs. Reading the article, he mentions Neovim keymaps once, plugins once, and color schemes four times. </p>\n<p>There's an idea in programming language design called <a href=\"https://wiki.haskell.org/Wadler's_Law\" target=\"_blank\">Wadler's Law</a>, which is that the majority of discussion is on syntax and not semantics. I imagine this applies to configuration too: the majority of time spent \"tinkering\" is on layout and coloring and not on changes to the tool's behavior. <strong>This is a trap.</strong> You get the most value of \"configuration\" when you use it to improve your workflow.</p>\n<p>Here's a Neovim script I wrote a while back:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"kr\">function</span> <span class=\"nf\">LoadLocal</span><span class=\"p\">(</span><span class=\"n\">local_task</span><span class=\"p\">)</span>\n <span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">b</span><span class=\"p\">.</span><span class=\"n\">local_task</span> <span class=\"o\">=</span> <span class=\"n\">local_task</span>\n<span class=\"kr\">end</span>\n\n<span class=\"kr\">function</span> <span class=\"nf\">RunLocal</span><span class=\"p\">()</span>\n <span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">cmd</span><span class=\"p\">(</span><span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">b</span><span class=\"p\">.</span><span class=\"n\">local_task</span><span class=\"p\">)</span>\n<span class=\"kr\">end</span>\n\n<span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">cmd</span> <span class=\"s\">[[command! -nargs=1 LoadLocal call v:lua.LoadLocal(<f-args>)]]</span>\n<span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">keymap</span><span class=\"p\">.</span><span class=\"n\">set</span><span class=\"p\">(</span><span class=\"s1\">'n'</span><span class=\"p\">,</span> <span class=\"s1\">'gxl'</span><span class=\"p\">,</span> <span class=\"n\">RunLocal</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"n\">desc</span><span class=\"o\">=</span><span class=\"s2\">\"Buffer Task\"</span><span class=\"p\">})</span>\n</code></pre></div>\n<p>On calling <code>:LoadLocal foo</code> it loads <code>foo</code> as the local command for that specific buffer. Then, typing <code>gxl</code> executes <code>foo</code>. The command can be <em>anything</em>, up to and including shelling out to <code>!rm *</code>.</p>\n<p>Oh, also any other script can set <code>vim.b.local_task</code>.<sup id=\"fnref:exploit\"><a class=\"footnote-ref\" href=\"#fn:exploit\">2</a></sup> <code>LoadLocal</code> can be used by the rest of my configs. When I open an XML file it sets the local task to the appropriate transformation script.</p>\n<p>Those eight lines have probably saved me at least eight hours of drudgework over their lifespan. <a href=\"https://www.hillelwayne.com/post/task-runner-neovim/\" target=\"_blank\">The full taskrunner has saved even more</a>. <em>That's</em> the kind of customization that's most useful.</p>\n<p>Anything that goes for an IDE goes double for the shell. I use a ton of shell scripts. </p>\n<h3>Customization teaches you</h3>\n<div class=\"subscribe-form\"></div>\n<p>A common argument against customization is that you shouldn't do it until you know the tooling basics well. For example, you shouldn't install a filetree plugin for neovim because it comes with Netrw. In other words, there's a difference between the skill of using your tools and the skill of customizing them, and the first is more important than the second.</p>\n<p>But just as often customization promotes mastery, by smoothing out the starting friction with using a feature. Say you're trying to get more comfortable with vim macros. Here's a keymapping that could help:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"n\">vim</span><span class=\"p\">.</span><span class=\"n\">keymap</span><span class=\"p\">.</span><span class=\"n\">set</span><span class=\"p\">(</span><span class=\"s1\">'n'</span><span class=\"p\">,</span> <span class=\"s2\">\"<leader>Q\"</span><span class=\"p\">,</span> <span class=\"s\">[[:let @q = input(\"Edit macro:\", @q)<CR>]]</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Vim macros are stored in a text registry: recording the <code>@q</code> macro will overwrite whatever you copied to the <code>\"q</code> register. This means that you can treat a macro <em>as</em> text. <code>input</code> will fill a prompt with the <code>@q</code> macro text, let you edit it as text, and then save your changes back to the register.</p>\n<p>This makes it so much easier to experiment with macros! If you screw up a macro while recording, you don't need to start over, you can just finish and make an edit afterwards. If you try a finished macro and it does something wrong, same thing, just undo and edit it.</p>\n<h3>Reasons not to customize</h3>\n<p>I've heard two other reasons why <em>not</em> to heavily customize your system:</p>\n<ol>\n<li>It'll be harder for other people to pair with you on your machine</li>\n<li>It'll be harder for you to work on machines without the customizations: servers you SSHed into, VMs, other people's computers, etc.</li>\n</ol>\n<p>(1) is only a problem if you're modifying default keybindings or adding surprising new ones. I deal with it by having two editors: a heavily customized Neovim for solo work, a vanilla VSCode (plus plugins) for collaboration.</p>\n<p>(2) is a bigger problem. I find the problem is how it messes with muscle memory. In vim I've mapped <code>H/L</code> to beginning/end of line when normally they mean \"first/last line in view\", and it always screws me up. It is so immensely frustrating. Other than that, though, I <em>generally</em> can tolerate losing my config for a bit. Most of the stuff that's most useful to me are things that aren't useful when I'm remoting, so I don't miss them. </p>\n<p>One good reason not to customize that I <em>haven't</em> heard: There is one more reason I've heard not to customize: you can easily break stuff that you don't know how to fix. This is more true with stuff like vim and shell that allow arbitrary scripts as config, less so with VSCode or the new crop of modal editors.</p>\n<div class=\"footnote\">\n<hr/>\n<ol>\n<li id=\"fn:caveat\">\n<p>Also <a href=\"https://arkadiuszchmura.com/posts/optimizing-your-workflow-does-matter-in-the-long-run/\" target=\"_blank\">he wrote positively about configuration later</a>. <a class=\"footnote-backref\" href=\"#fnref:caveat\" title=\"Jump back to footnote 1 in the text\">↩</a></p>\n</li>\n<li id=\"fn:exploit\">\n<p>I swear to God if this leads to someone targeting an exploit at me I will quit programming forever <a class=\"footnote-backref\" href=\"#fnref:exploit\" title=\"Jump back to footnote 2 in the text\">↩</a></p>\n</li>\n</ol>\n</div>", "url": "https://buttondown.com/hillelwayne/archive/keep-perfecting-your-config/", "published": "2024-07-16T19:18:22.000Z", "updated": "2024-07-16T19:18:22.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/", "title": "Logic for Programmers now in early access!", "description": "<p>I am delighted to announce that <a href=\"https://leanpub.com/logic/\" target=\"_blank\">Logic for Programmers</a> is now available for purchase! While still in early access, it's almost 20,000 words, has 30 exercises, and covers a wide variety of logic applications: </p>\n<ul>\n<li>Property testing</li>\n<li>Functional correctness and contracts</li>\n<li>Formal verification</li>\n<li>Decision tables</li>\n<li>Database constraints</li>\n<li>Data modeling and alloy</li>\n<li>Constraint solving</li>\n<li>Logic programming</li>\n</ul>\n<p>I want to emphasize the book is early access; meaning it's not close to done yet. I'm releasing it now so I can get feedback from readers and use that to decide what I write next. But I'm only willing to release it because I think, even in its current state, it's worth reading. I'm just promising it'll be even better in a few months. </p>\n<h2>What's next</h2>\n<p>I think exercise solutions are broken on epub, so step one is getting that fixed. </p>\n<p>After that, I'm taking a short break. I put basically all of June into working on this and need to get back to blogging and client acquisition. Plus I need a bit of time to gather reader feedback. </p>\n<p>I'm thinking of new major releases being either every two weeks or every month or so. I'll upload new versions of the book between releases and then send a Leanpub publication announcement when we hit the next milestone.</p>\n<h3>PS</h3>\n<p>There's a free book coupon on the <a href=\"https://www.patreon.com/hillelwayne\" target=\"_blank\">Patreon</a>. Enough people have joined that I feel obligated to start posting more original content there <em>why does this keep happening to me</em></p>", "url": "https://buttondown.com/hillelwayne/archive/logic-for-programmers-now-in-early-access/", "published": "2024-07-08T17:33:18.000Z", "updated": "2024-07-08T17:33:18.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] }, { "id": "https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/", "title": "Solving a math problem with planner programming", "description": "<p>The deadline for the <a href=\"https://buttondown.email/hillelwayne/archive/logic-for-programmers-update/\" target=\"_blank\">logic book</a> is coming up! I'm hoping to have it ready for early access by either the end of this week or early next week. During a break on Monday I saw this interesting problem on <a href=\"https://math.stackexchange.com/questions/4939319/how-many-steps-are-needed-to-turn-one-a-into-at-least-100-000-as-using-only\" target=\"_blank\">Math Stack Exchange</a>:</p>\n<blockquote>\n<p>Suppose that at the beginning there is a blank document, and a letter \"a\" is written in it. In the following steps, only the three functions of \"select all\", \"copy\" and \"paste\" can be used. </p>\n<p>Find the minimum number of steps to reach at least 100,000 a's (each of the three operations of \"select all\", \"copy\" and \"paste\" is counted as one step). If the target number is not specified, and I want to get the exact amount of a, is there a general formula?</p>\n</blockquote>\n<p>The first two answers look for analytic solutions. The <a href=\"https://math.stackexchange.com/a/4939673\" target=\"_blank\">last answer</a> shares a C++ program that finds it via breadth-first search. I'll reproduce it here:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"cp\">#include</span><span class=\"w\"> </span><span class=\"cpf\"><iostream></span>\n<span class=\"cp\">#include</span><span class=\"w\"> </span><span class=\"cpf\"><queue></span>\n\n<span class=\"k\">enum</span><span class=\"w\"> </span><span class=\"nc\">Mode</span>\n<span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"n\">SELECT</span><span class=\"p\">,</span>\n<span class=\"w\"> </span><span class=\"n\">COPY</span><span class=\"p\">,</span>\n<span class=\"w\"> </span><span class=\"n\">PASTE</span>\n<span class=\"p\">};</span>\n\n<span class=\"k\">struct</span><span class=\"w\"> </span><span class=\"nc\">Node</span>\n<span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"n\">noOfAs</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"n\">steps</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"n\">noOfAsCopied</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"n\">Mode</span><span class=\"w\"> </span><span class=\"n\">mode</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n\n<span class=\"kt\">int</span><span class=\"w\"> </span><span class=\"nf\">main</span><span class=\"p\">()</span>\n<span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">queue</span><span class=\"o\"><</span><span class=\"n\">Node</span><span class=\"o\">></span><span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">;</span>\n\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">push</span><span class=\"p\">({</span><span class=\"mi\">1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">SELECT</span><span class=\"p\">});</span>\n\n<span class=\"w\"> </span><span class=\"k\">while</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">empty</span><span class=\"p\">())</span>\n<span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"n\">Node</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">front</span><span class=\"p\">();</span>\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">pop</span><span class=\"p\">();</span>\n\n<span class=\"w\"> </span><span class=\"k\">if</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"w\"> </span><span class=\"o\">>=</span><span class=\"w\"> </span><span class=\"mi\">100000</span><span class=\"p\">)</span>\n<span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">cout</span><span class=\"w\"> </span><span class=\"o\"><<</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">steps</span><span class=\"w\"> </span><span class=\"o\"><<</span><span class=\"w\"> </span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">endl</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"k\">break</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"p\">}</span>\n\n<span class=\"w\"> </span><span class=\"k\">switch</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">mode</span><span class=\"p\">)</span>\n<span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\"> </span><span class=\"k\">case</span><span class=\"w\"> </span><span class=\"no\">SELECT</span><span class=\"p\">:</span>\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">push</span><span class=\"p\">({</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">steps</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAsCopied</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">COPY</span><span class=\"p\">});</span>\n<span class=\"w\"> </span><span class=\"k\">break</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"k\">case</span><span class=\"w\"> </span><span class=\"no\">COPY</span><span class=\"p\">:</span>\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">push</span><span class=\"p\">({</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">steps</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">PASTE</span><span class=\"p\">});</span>\n<span class=\"w\"> </span><span class=\"k\">break</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"k\">case</span><span class=\"w\"> </span><span class=\"no\">PASTE</span><span class=\"p\">:</span>\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">push</span><span class=\"p\">({</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">steps</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAsCopied</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">SELECT</span><span class=\"p\">});</span>\n<span class=\"w\"> </span><span class=\"n\">q</span><span class=\"p\">.</span><span class=\"n\">push</span><span class=\"p\">({</span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAs</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAsCopied</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">steps</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">n</span><span class=\"p\">.</span><span class=\"n\">noOfAsCopied</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"n\">PASTE</span><span class=\"p\">});</span>\n<span class=\"w\"> </span><span class=\"k\">break</span><span class=\"p\">;</span>\n<span class=\"w\"> </span><span class=\"p\">}</span>\n<span class=\"w\"> </span><span class=\"p\">}</span>\n\n<span class=\"w\"> </span><span class=\"k\">return</span><span class=\"w\"> </span><span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div>\n<p>This is guaranteed to find a shortest possible solution due to a fun property of BFS: the distance of nodes to the origin never decreases. If you evaluate Node Y after Node X, then Y.dist >= X.dist, meaning that the first valid solution will be a shortest possible solution. I should make this into a logic book example! </p>\n<p>This also has the drawback of preventing <a href=\"https://www.hillelwayne.com/post/cleverness/\" target=\"_blank\">the use of an insight</a>. We <em>should</em> be able to fuse the select and copy steps together, meaning instead of having three actions (select, copy, paste) we only need two (selectcopy, paste), where selectcopy takes twice as many steps as pasting.</p>\n<p>But we can't make that optimization because it breaks monotonicity. We're now pushing a mix of <code>n+1</code> and <code>n+2</code> steps onto the queue, and there's no way to order things to guarantee all of the <code>n+1</code> steps are searched before any <code>n+2</code> step.</p>\n<p>I thought I'd try to solve it with planning language instead, so we can get both the elegant solution and the optimization.</p>\n<h3>Planning</h3>\n<div class=\"subscribe-form\"></div>\n<p>The rough idea of planning is that you provide an initial state, a set of actions, and a target, and the tool finds the shortest sequence of actions that reaches the target. I've written about it in-depth <a href=\"https://www.hillelwayne.com/post/picat/\" target=\"_blank\">here</a> and also a comparison of planning to model checking <a href=\"https://www.hillelwayne.com/post/picat/\" target=\"_blank\">here</a>. I like how some tough problems in imperative and functional paradigms become easy problems with planning. </p>\n<p>This is all in <a href=\"http://picat-lang.org/\" target=\"_blank\">Picat</a>, by the way, which I've talked about more <a href=\"https://buttondown.email/hillelwayne/archive/picat-is-my-favorite-new-toolbox-language/\" target=\"_blank\">here</a> and in the <a href=\"https://www.hillelwayne.com/post/picat/\" target=\"_blank\">planning piece</a>. I'll just be explaining the planning stuff specific to this problem.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"s s-Atom\">import</span> <span class=\"s s-Atom\">planner</span><span class=\"p\">.</span>\n<span class=\"s s-Atom\">import</span> <span class=\"s s-Atom\">util</span><span class=\"p\">.</span>\n\n<span class=\"s s-Atom\">main</span> <span class=\"s s-Atom\">=></span>\n <span class=\"nv\">Init</span> <span class=\"o\">=</span> <span class=\"err\">$</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"c1\">% one a, nothing copied</span>\n <span class=\"p\">,</span> <span class=\"nf\">best_plan</span><span class=\"p\">(</span><span class=\"nv\">Init</span><span class=\"p\">,</span> <span class=\"nv\">Plan</span><span class=\"p\">,</span> <span class=\"nv\">Cost</span><span class=\"p\">)</span>\n <span class=\"p\">,</span> <span class=\"s s-Atom\">nl</span>\n <span class=\"p\">,</span> <span class=\"nf\">printf</span><span class=\"p\">(</span><span class=\"s2\">\"Cost=%d%n\"</span><span class=\"p\">,</span> <span class=\"nv\">Cost</span><span class=\"p\">)</span>\n <span class=\"p\">,</span> <span class=\"nf\">printf</span><span class=\"p\">(</span><span class=\"s2\">\"Plan=%s%n\"</span><span class=\"p\">,</span> <span class=\"nf\">join</span><span class=\"p\">([</span><span class=\"nv\">P</span><span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span><span class=\"s s-Atom\">:</span> <span class=\"nv\">P</span> <span class=\"s s-Atom\">in</span> <span class=\"nv\">Plan</span><span class=\"p\">],</span> <span class=\"s2\">\" \"</span><span class=\"p\">))</span>\n <span class=\"p\">.</span>\n</code></pre></div>\n<p>We're storing the state of the system as two integers: the number of characters printed and the number of characters on our clipboard. Since we'll be fusing selects and copies, we don't need to also track the number of characters selected <del>(unlike the C++)</del>.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"nf\">final</span><span class=\"p\">(</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">A</span><span class=\"p\">,</span> <span class=\"k\">_</span><span class=\"p\">))</span> <span class=\"s s-Atom\">=></span> <span class=\"nv\">A</span> <span class=\"o\">>=</span> <span class=\"mf\">100000.</span>\n\n<span class=\"nf\">action</span><span class=\"p\">(</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">A</span><span class=\"p\">,</span> <span class=\"nv\">Clipboard</span><span class=\"p\">),</span> <span class=\"nv\">To</span><span class=\"p\">,</span> <span class=\"nv\">Action</span><span class=\"p\">,</span> <span class=\"nv\">Cost</span><span class=\"p\">)</span> <span class=\"s s-Atom\">?=></span>\n <span class=\"nv\">NewA</span> <span class=\"o\">=</span> <span class=\"nv\">A</span> <span class=\"o\">+</span> <span class=\"nv\">Clipboard</span>\n <span class=\"p\">,</span> <span class=\"nv\">To</span> <span class=\"o\">=</span> <span class=\"err\">$</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">NewA</span><span class=\"p\">,</span> <span class=\"nv\">Clipboard</span><span class=\"p\">)</span>\n <span class=\"p\">,</span> <span class=\"nv\">Action</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">\"P\"</span><span class=\"p\">,</span> <span class=\"nv\">To</span><span class=\"p\">}</span>\n <span class=\"p\">,</span> <span class=\"nv\">Cost</span> <span class=\"o\">=</span> <span class=\"mi\">1</span>\n <span class=\"p\">.</span>\n</code></pre></div>\n<p>The paste action just adds the clipboard to the character count. Because Picat is a research language it's a little weird with putting expressions inside structures. If we did <code>$state(1 + 1)</code> it would store it as <em>literally</em> <code>$state(1 + 1)</code>, not <code>state(2)</code>.</p>\n<p>Also you have to use dollar signs for definitions but no dollar signs for pattern matching inside a function definition. I have <em>no idea</em> why.</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"nf\">action</span><span class=\"p\">(</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">A</span><span class=\"p\">,</span> <span class=\"nv\">Clipboard</span><span class=\"p\">),</span> <span class=\"nv\">To</span><span class=\"p\">,</span> <span class=\"nv\">Action</span><span class=\"p\">,</span> <span class=\"nv\">Cost</span><span class=\"p\">)</span> <span class=\"s s-Atom\">?=></span>\n <span class=\"nv\">To</span> <span class=\"o\">=</span> <span class=\"err\">$</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">A</span><span class=\"p\">,</span> <span class=\"nv\">A</span><span class=\"p\">)</span>\n <span class=\"p\">,</span> <span class=\"nv\">Action</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">\"SC\"</span><span class=\"p\">,</span> <span class=\"nv\">To</span><span class=\"p\">}</span>\n <span class=\"p\">,</span> <span class=\"nv\">Cost</span> <span class=\"o\">=</span> <span class=\"mi\">2</span>\n <span class=\"p\">.</span>\n</code></pre></div>\n<p>And that's it! That's the whole program. \nRunning this gives us:</p>\n<div class=\"codehilite\"><pre><span></span><code>Cost=42\nPlan=SC P P SC P P SC P P SC P P SC P P SC \n P P SC P P SC P P SC P P P SC P P P\n</code></pre></div>\n<p>To find if there's a sequence that gets us <em>exactly</em> 100,000, we just need to make one change:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"gd\">- final(state(A, _)) => A >= 100000.</span>\n<span class=\"gi\">+ final(state(A, _)) => A = 100000.</span>\n</code></pre></div>\n<p>This returns a cost of 43.</p>\n<p>On the other hand, I can't get it to find a path that makes exactly <code>100,001</code> characters, even with some optimizations. This is because the shortest path is over 9000 steps long! I haven't checked if the C++ BFS can find it.</p>\n<h3>Metaplanning</h3>\n<p>One reason planning fascinates me so much is that, if a problem is now easy, you can play around with it. Like if I wanted to add \"delete a character\" as a move, that's easy:</p>\n<div class=\"codehilite\"><pre><span></span><code><span class=\"nf\">action</span><span class=\"p\">(</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">A</span><span class=\"p\">,</span> <span class=\"nv\">Clipboard</span><span class=\"p\">),</span> <span class=\"nv\">To</span><span class=\"p\">,</span> <span class=\"nv\">Action</span><span class=\"p\">,</span> <span class=\"nv\">Cost</span><span class=\"p\">)</span> <span class=\"s s-Atom\">?=></span>\n <span class=\"nv\">A</span> <span class=\"o\">></span> <span class=\"mi\">0</span>\n <span class=\"p\">,</span> <span class=\"nv\">NewA</span> <span class=\"o\">=</span> <span class=\"nv\">A</span> <span class=\"o\">-</span> <span class=\"mi\">1</span>\n <span class=\"p\">,</span> <span class=\"nv\">To</span> <span class=\"o\">=</span> <span class=\"err\">$</span><span class=\"nf\">state</span><span class=\"p\">(</span><span class=\"nv\">NewA</span><span class=\"p\">,</span> <span class=\"nv\">Clipboard</span><span class=\"p\">)</span>\n <span class=\"p\">,</span> <span class=\"nv\">Action</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">\"D\"</span><span class=\"p\">,</span> <span class=\"nv\">To</span><span class=\"p\">}</span>\n <span class=\"p\">,</span> <span class=\"nv\">Cost</span> <span class=\"o\">=</span> <span class=\"mi\">1</span>\n <span class=\"p\">.</span>\n</code></pre></div>\n<p>This doesn't make exceeding or reaching 100,000 easier, but it makes reaching 100,001 take 47 steps instead of 9000.</p>\n<p>With some tweaks, I can also ask questions like \"what numbers does it make <em>the most</em> easier?\" or \"Do some numbers have more than one shortest path? Which number has the most?\"</p>\n<p>Planning is really cool.</p>", "url": "https://buttondown.com/hillelwayne/archive/solving-a-math-problem-with-planner-programming/", "published": "2024-07-02T15:46:13.000Z", "updated": "2024-07-02T15:46:13.000Z", "content": null, "image": null, "media": [], "authors": [], "categories": [] } ] }