<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>What Comes to Mind</title><link>https://sdowney.org/</link><description>Stuff, and more stuff</description><atom:link href="https://sdowney.org/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:sdowney@sdowney.dev"&gt;Steve Downey&lt;/a&gt; 
 &lt;a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;
 &lt;img alt="Creative Commons License BY-NC-SA"
 style="border-width:0; margin-bottom:12px;"
 src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"&gt;&lt;/a&gt;</copyright><lastBuildDate>Mon, 29 Jun 2026 12:50:41 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>An Infix Backtick Operator for C++</title><link>https://sdowney.org/posts/infix-backtick-operator/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="org5d2e159"&gt;
&lt;p&gt;
I am working on a proposal to let any callable be written between two backticks
as an infix binary operator: &lt;code&gt;x `f` y&lt;/code&gt; means exactly &lt;code&gt;f(x, y)&lt;/code&gt;. It is borrowed
from Haskell, it desugars to an ordinary call, and I have it working in both
Clang and GCC. This post is the announcement and the design tour.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-orgefa3cbf" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgefa3cbf"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; The pitch in one line&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
C++ lets you spell a handful of binary operations infix — &lt;code&gt;a + b&lt;/code&gt;, &lt;code&gt;a &amp;lt; b&lt;/code&gt;,
&lt;code&gt;a | b&lt;/code&gt; — and lets you overload them. Everything else is a call: &lt;code&gt;min(a, b)&lt;/code&gt;,
&lt;code&gt;dot(u, v)&lt;/code&gt;, &lt;code&gt;gcd(m, n)&lt;/code&gt;, &lt;code&gt;approx_equal(x, y)&lt;/code&gt;. The proposal is to erase that
distinction at the call site. Put a callable between backticks and it becomes a
binary operator:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;a `plus` b              &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;plus(a, b)
&lt;/span&gt;a `&lt;span class="org-constant"&gt;std&lt;/span&gt;::min` b          &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;std::min(a, b)
&lt;/span&gt;m `at` k                &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;at(m, k)
&lt;/span&gt;u `dot` v               &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;dot(u, v)
&lt;/span&gt;x `approx_equal` y      &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;approx_equal(x, y)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The text between the backticks is not a name the language knows about. It is an
arbitrary call-eligible expression, and the whole thing is &lt;b&gt;defined&lt;/b&gt; to be the
call you would have written anyway. There is no new overloadable
&lt;code&gt;operator`&lt;/code&gt; , no operator table to register, no fixity declarations. &lt;code&gt;x `f` y&lt;/code&gt;
is &lt;code&gt;f(x, y)&lt;/code&gt;, and that is the entire semantic.
&lt;/p&gt;

&lt;p&gt;
Haskell programmers will recognise this immediately: &lt;code&gt;`div`&lt;/code&gt;, &lt;code&gt;`mod`&lt;/code&gt;, &lt;code&gt;`elem`&lt;/code&gt;
are exactly this construction. The backtick is available in C++ because today it
has no meaning outside string, character, and raw-string literals, all of which
are lexed before the punctuator stage and are therefore untouched.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgdb90860" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgdb90860"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; It desugars to a call, and that is the whole point&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
The single most important design decision is that the operator lowers to a
&lt;b&gt;call expression&lt;/b&gt; in the front end, before overload resolution runs. It is not a
macro, not a syntactic rewrite with its own rules, not a new AST category with
its own type rules. It builds the same call node the compiler would build for
&lt;code&gt;f(x, y)&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
That one choice means the feature inherits, for free and correctly:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;overload resolution and ADL,&lt;/li&gt;
&lt;li&gt;templates and SFINAE,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constexpr&lt;/code&gt; evaluation,&lt;/li&gt;
&lt;li&gt;conversions, value categories, and lifetime,&lt;/li&gt;
&lt;li&gt;code generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
None of it is re-implemented. If &lt;code&gt;f(x, y)&lt;/code&gt; compiles and does a thing, then
&lt;code&gt;x `f` y&lt;/code&gt; compiles and does the same thing. If &lt;code&gt;f(x, y)&lt;/code&gt; is ambiguous or
ill-formed, so is the backtick form, with the same diagnostic. The feature has
essentially no semantics of its own to get wrong, which is exactly what you want
from sugar.
&lt;/p&gt;

&lt;p&gt;
A few consequences fall straight out of "it is just the call":
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;x `T` y                 &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;== T(x, y) : functional-style construction; CTAD applies
&lt;/span&gt;a `&lt;span class="org-constant"&gt;std&lt;/span&gt;::pair` b         &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;== std::pair(a, b)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
A type name is callable, so a type in the slot constructs. Evaluation order is
the call's evaluation order: operand order is unspecified, and since C++17 the
callee — here, the thing between the backticks — is sequenced before both
operands, even though it is written between them. Nothing new to learn; if you
know how a call behaves, you know how this behaves.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org86cafda" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org86cafda"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; How it parses&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
Two questions decide how an infix operator feels: how tightly it binds, and how
a chain groups.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Precedence.&lt;/b&gt; Backtick is the highest-precedence &lt;b&gt;binary&lt;/b&gt; operator — tighter
than &lt;code&gt;*&lt;/code&gt;, looser than the unary/prefix operators. Both operands are
cast-expressions, so prefix operators attach symmetrically:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;-a `f` -b               &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;f(-a, -b)     symmetric, like every other binary op
&lt;/span&gt;a * b `f` c             &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;a * f(b, c)   binds tighter than *&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
I considered making it bind tighter than unary minus so that &lt;code&gt;-x `f` y&lt;/code&gt; would
mean &lt;code&gt;-f(x, y)&lt;/code&gt;. That was rejected: it would make backtick the only operator in
the language where a leading &lt;code&gt;-&lt;/code&gt; floats &lt;b&gt;out&lt;/b&gt; of its operand, so &lt;code&gt;-a `f` -b&lt;/code&gt;
would mean &lt;code&gt;-f(a, -b)&lt;/code&gt; — asymmetric and hard to teach. Matching every other
binary operator won.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Associativity.&lt;/b&gt; Left, like reading order:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;a `f` b `g` c           &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;g(f(a, b), c)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;b&gt;The slot.&lt;/b&gt; The thing between the backticks is parsed as an
assignment-expression — anything you could write as the callee of a call,
excluding a top-level comma. Qualified names, member-access expressions, even a
lambda, are all fine.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgb6a3246" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgb6a3246"&gt;&lt;span class="section-number-2"&gt;4.&lt;/span&gt; The one genuinely tricky part&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;p&gt;
The open and close delimiter are the same token. A naive parser, having read
&lt;code&gt;x `f&lt;/code&gt;, would see the closing backtick and think a &lt;b&gt;second&lt;/b&gt; backtick operator
was starting. C++ already has exactly this problem with &lt;code&gt;&amp;gt;&lt;/code&gt; inside template
argument lists — &lt;code&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&lt;/code&gt; — and solves it with a parser flag that
says "right now, &lt;code&gt;&amp;gt;&lt;/code&gt; is not an operator." I do the same thing: a
&lt;code&gt;BacktickIsOperator&lt;/code&gt; flag in Clang (modelled on &lt;code&gt;GreaterThanIsOperator&lt;/code&gt;), a
&lt;code&gt;backtick_is_operator_p&lt;/code&gt; in GCC (modelled on &lt;code&gt;greater_than_is_operator_p&lt;/code&gt;),
false while parsing the slot and restored inside nested parentheses.
&lt;/p&gt;

&lt;p&gt;
A side effect of the same-token delimiter is that there is no such thing as a
bare nested backtick. The first interior backtick always closes the slot, so
what looks like "nesting" is &lt;b&gt;token-identical&lt;/b&gt; to an ordinary left-associative
chain — and a chain is exactly what it parses as. To actually nest, you
parenthesise:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;x `f `g` h` y           &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;a chain:  h(f(x, g), y)
&lt;/span&gt;x `(f `g` h)` y         &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;nested:   (g(f, h))(x, y)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This is the same answer-changes-on-regrouping situation as &lt;code&gt;a - b - c&lt;/code&gt; versus
&lt;code&gt;a - (b - c)&lt;/code&gt;, which no compiler diagnoses because subtraction is
left-associative and the parentheses just override the grouping. Backtick is no
different. The language defends against honest mistakes, not against someone who
builds a type that is simultaneously callable, value-convertible, and
asymmetric and then omits the parentheses on purpose.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgb276d5e" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgb276d5e"&gt;&lt;span class="section-number-2"&gt;5.&lt;/span&gt; A second use for the same token: escaping keywords&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-5"&gt;
&lt;p&gt;
Because the proposal claims the backtick as a punctuator, it is worth asking
what &lt;b&gt;else&lt;/b&gt; the token could do, and there is one obvious companion use: escaping
a keyword so it can name an entity. This is the escape hatch that lets a
&lt;b&gt;future&lt;/b&gt; keyword avoid breaking existing code that already used that word as an
identifier — Swift's &lt;code&gt;`class`&lt;/code&gt;, Kotlin's backtick identifiers, F#'s
double-backtick names, Rust's &lt;code&gt;r#&lt;/code&gt; raw identifiers all do a version of it.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-type"&gt;void&lt;/span&gt; `&lt;span class="org-keyword"&gt;new&lt;/span&gt;`();           &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;declares a function named "new"
&lt;/span&gt;`&lt;span class="org-keyword"&gt;new&lt;/span&gt;`(a, b);            &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;calls it
&lt;/span&gt;obj.`&lt;span class="org-keyword"&gt;delete&lt;/span&gt;`();         &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;member named "delete"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The two uses never collide because they live in mutually exclusive grammatical
positions. In operand, primary, or declarator position the grammar wants a name,
so a backtick opens a keyword-escape. In post-operand position the grammar wants
a binary operator, so a backtick is the infix operator. This is the same
position-based disambiguation C++ already does for &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, and &lt;code&gt;&amp;lt;&lt;/code&gt;. The escape
yields an ordinary identifier, so lookup, mangling, linkage, and ABI are
unchanged — &lt;code&gt;void `new`();&lt;/code&gt; links as a function named &lt;code&gt;new&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
The two uses share one lexical token and one committee, so I am proposing them
together, in one paper, rather than letting two independent designs drift into
contradiction.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org47d21c7" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org47d21c7"&gt;&lt;span class="section-number-2"&gt;6.&lt;/span&gt; What it is good for&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-6"&gt;
&lt;p&gt;
The shallow answer is readability for binary operations that read better infix:
&lt;code&gt;lo `clamp` hi&lt;/code&gt;, &lt;code&gt;a `gcd` b&lt;/code&gt;, &lt;code&gt;p `implies` q&lt;/code&gt;, &lt;code&gt;lhs `approx_equal` rhs&lt;/code&gt;,
&lt;code&gt;set_a `intersect` set_b&lt;/code&gt;. Named operations stop being visually second-class to
the dozen built-in symbols.
&lt;/p&gt;

&lt;p&gt;
The deeper answer is that the slot is an arbitrary callable and the operator
chains, and those two facts reach further than they first appear. A one-line
helper turns backtick into a left-to-right threading operator:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;inline&lt;/span&gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-variable-name"&gt;pipe&lt;/span&gt; =
    [](&lt;span class="org-keyword"&gt;auto&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;x&lt;/span&gt;, &lt;span class="org-keyword"&gt;auto&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;) -&amp;gt; &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(&lt;span class="org-keyword"&gt;auto&lt;/span&gt;)
    { &lt;span class="org-keyword"&gt;return&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::invoke(&lt;span class="org-constant"&gt;std&lt;/span&gt;::forward&amp;lt;&lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(f)&amp;gt;(f),
                         &lt;span class="org-constant"&gt;std&lt;/span&gt;::forward&amp;lt;&lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(x)&amp;gt;(x)); };

x `pipe` f `pipe` g `pipe` h        &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;h(g(f(x))) — data-flow order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The decisive case is that this drives the &lt;b&gt;existing&lt;/b&gt; range adaptor closures
unchanged. &lt;code&gt;views::filter(pred)&lt;/code&gt; and &lt;code&gt;views::transform(fn)&lt;/code&gt; are already unary
callables — &lt;code&gt;c | a&lt;/code&gt; is defined as &lt;code&gt;a(c)&lt;/code&gt; — so &lt;code&gt;pipe&lt;/code&gt; feeds them directly, same
result and same laziness as the &lt;code&gt;|&lt;/code&gt; pipe, with no bespoke &lt;code&gt;operator|&lt;/code&gt; overloads
involved:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;r `pipe` &lt;span class="org-constant"&gt;views&lt;/span&gt;::filter(pred) `pipe` &lt;span class="org-constant"&gt;views&lt;/span&gt;::transform(fn)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And it reopens something the language otherwise reserves to a fixed set of
built-ins. You cannot get short-circuit evaluation back by overloading &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; or
&lt;code&gt;||&lt;/code&gt; — an overloaded one evaluates both sides. But a helper whose right operand
is a &lt;b&gt;callable&lt;/b&gt; controls whether and when that side runs:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;short-circuiting logical implication:  p ==&amp;gt; q  ≡  !p || q
&lt;/span&gt;&lt;span class="org-keyword"&gt;inline&lt;/span&gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-variable-name"&gt;implies&lt;/span&gt; =
    [](&lt;span class="org-type"&gt;bool&lt;/span&gt; &lt;span class="org-variable-name"&gt;p&lt;/span&gt;, &lt;span class="org-keyword"&gt;auto&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;q&lt;/span&gt;) -&amp;gt; &lt;span class="org-type"&gt;bool&lt;/span&gt; { &lt;span class="org-keyword"&gt;return&lt;/span&gt; &lt;span class="org-negation-char"&gt;!&lt;/span&gt;p || q(); };

p `implies` [&amp;amp;]{ &lt;span class="org-keyword"&gt;return&lt;/span&gt; expensive(); }   &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;q() runs only when p holds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The monadic / fallible chain is just the zero-ceremony special case of this,
where the stages are already functions:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;parse(s) `mbind` validate `mbind` store;   &lt;span class="org-comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="org-comment"&gt;stops at the first error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
None of these helpers are part of the proposal — the paper is language-only, and
each helper is a few lines of ordinary user code. They are here as &lt;b&gt;motivation&lt;/b&gt;,
to show the operator's reach. Field experience can later decide which, if any,
earn a place in a library.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org84b6ce3" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org84b6ce3"&gt;&lt;span class="section-number-2"&gt;7.&lt;/span&gt; It is not the pipeline operator, and does not want to be&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-7"&gt;
&lt;p&gt;
The obvious neighbour is Barry Revzin's &lt;code&gt;|&amp;gt;&lt;/code&gt; (P2011, the "pizza" operator). Both
bottom out in a call and their two-argument cases coincide — &lt;code&gt;a `plus` b&lt;/code&gt;,
&lt;code&gt;a |&amp;gt; plus(b)&lt;/code&gt;, and &lt;code&gt;plus(a, b)&lt;/code&gt; are the same call. But they are different
shapes and they compose rather than compete:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;Backtick is &lt;b&gt;symmetric binary infix&lt;/b&gt;: the callee sits between two operands,
the result is an overload-resolved call, and the right-hand side is a single
value. Beyond two operands it cannot thread.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|&amp;gt;&lt;/code&gt; is a &lt;b&gt;non-overloadable syntactic rewrite&lt;/b&gt;: &lt;code&gt;x |&amp;gt; f(a, b, c)&lt;/code&gt; prepends &lt;code&gt;x&lt;/code&gt;
to an arbitrary-arity call. It binds low, threads through stages, and is the
right tool for chains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
So backtick supplies infix detail &lt;b&gt;inside&lt;/b&gt; a pipeline stage, &lt;code&gt;|&amp;gt;&lt;/code&gt; threads the
value &lt;b&gt;between&lt;/b&gt; stages:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;r |&amp;gt; filter([](&lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-variable-name"&gt;e&lt;/span&gt;){ &lt;span class="org-keyword"&gt;return&lt;/span&gt; e `mod` 2 `eq` 0; }) |&amp;gt; sum()
&lt;span class="org-comment-delimiter"&gt;//                              &lt;/span&gt;&lt;span class="org-comment"&gt;\____ eq(mod(e, 2), 0) ____/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Backtick deliberately leaves the &lt;code&gt;|&amp;gt;&lt;/code&gt; spelling unspelled so the two can coexist
in one program.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org9c5f106" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org9c5f106"&gt;&lt;span class="section-number-2"&gt;8.&lt;/span&gt; Implementation status&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-8"&gt;
&lt;p&gt;
This is not a paper design with a hand-wave at implementability. The operator
and the keyword-escape are both prototyped, gated behind an opt-in &lt;code&gt;-fbacktick&lt;/code&gt;
flag, in &lt;b&gt;two&lt;/b&gt; independent compilers. Both forks are public, on a &lt;code&gt;backtick&lt;/code&gt;
branch:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;b&gt;Clang&lt;/b&gt; (&lt;a href="https://github.com/steve-downey/llvm-project/tree/backtick"&gt;steve-downey/llvm-project @ backtick&lt;/a&gt;) — lexer token, precedence
level, parser hook, Sema desugar to a &lt;code&gt;CallExpr&lt;/code&gt;, driver flag, a transparent
AST wrapper so &lt;code&gt;-ast-print&lt;/code&gt; round-trips the surface syntax, and clang-format
support for both uses. Full ADL. Tests live under
&lt;a href="https://github.com/steve-downey/llvm-project/tree/backtick/clang/test"&gt;&lt;code&gt;clang/test/&lt;/code&gt;&lt;/a&gt; (the &lt;code&gt;backtick-*&lt;/code&gt; files in &lt;code&gt;Parser/&lt;/code&gt;, &lt;code&gt;Lexer/&lt;/code&gt;, and
&lt;code&gt;SemaCXX/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GCC&lt;/b&gt; (&lt;a href="https://github.com/steve-downey/gcc/tree/backtick"&gt;steve-downey/gcc @ backtick&lt;/a&gt;) — &lt;code&gt;libcpp&lt;/code&gt; token, parser precedence
and slot handling, desugar via &lt;code&gt;finish_call_expr&lt;/code&gt;, the same flag, and full ADL
on the slot. Tests live under
&lt;a href="https://github.com/steve-downey/gcc/tree/backtick/gcc/testsuite/g%2B%2B.dg/backtick"&gt;&lt;code&gt;gcc/testsuite/g++.dg/backtick/&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Two independent implementations that agree is the strongest evidence of
implementability I can bring to EWG/CWG. Every pipeline pattern above was
compiled and run against the built &lt;code&gt;-fbacktick&lt;/code&gt; Clang, so the §"what it is good
for" claims are implementation experience, not assertion.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org8c9f6fc" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org8c9f6fc"&gt;&lt;span class="section-number-2"&gt;9.&lt;/span&gt; What I am proposing, and what comes next&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-9"&gt;
&lt;p&gt;
A pure core-language feature, targeting C++29: the infix backtick operator plus
the keyword-escape, in one paper, with no standard-library additions. The
desugar-to-a-call MVP is the whole language change; the AST wrapper and a
Compiler Explorer deployment strengthen the story but do not block it.
&lt;/p&gt;

&lt;p&gt;
If you have a binary operation that has always read worse as &lt;code&gt;f(x, y)&lt;/code&gt; than it
would as &lt;code&gt;x `f` y&lt;/code&gt; — a domain predicate, a metric, a combinator — that is the
use case, and I would like to hear about it. The two compiler forks linked above
are public if you want to build the branch and try it; comments, objections, and
motivating examples are all welcome — reach me at &lt;a href="mailto:sdowney@gmail.com"&gt;sdowney@gmail.com&lt;/a&gt; — before this
goes in front of the committee.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/infix-backtick-operator/</guid><pubDate>Mon, 29 Jun 2026 12:00:00 GMT</pubDate></item><item><title>Refreshing a Stale Git Subtree</title><link>https://sdowney.org/posts/migrating-wg21-subtree/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="org0bae923"&gt;
&lt;p&gt;
I write my WG21 papers with &lt;a href="https://github.com/mpark/wg21"&gt;MPark/WG21&lt;/a&gt;, a Pandoc-based framework I vendor into the paper repo as a git subtree. The framework had a major overhaul–the build system split apart, and Pandoc jumped from 2.18 to 3.9–and my copy was 98 commits behind. Worse, the subtree had drifted in two directions at once: real local patches for a TLS-intercepting corporate network, and a pile of pointless autoformatter churn from my own pre-commit hooks. This is how I dragged it back to a verbatim copy of upstream, moved to the new &lt;code&gt;flat.mk&lt;/code&gt; include, and pushed every local change back &lt;i&gt;out&lt;/i&gt; of the subtree so the next update is a one-liner.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-org2f8f597" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org2f8f597"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; What the overhaul changed&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
A few things upstream matter to anyone who integrates MPark/WG21.
&lt;/p&gt;

&lt;p&gt;
The Make machinery was split. The old monolithic &lt;code&gt;Makefile&lt;/code&gt;–the one you used via &lt;code&gt;include wg21/Makefile&lt;/code&gt;–became three fragments: &lt;code&gt;wg21.mk&lt;/code&gt; is the engine you never include directly, &lt;code&gt;flat.mk&lt;/code&gt; is for all-papers-in-one-directory, and &lt;code&gt;paper.mk&lt;/code&gt; is for one-directory-per-paper. The old &lt;code&gt;include wg21/Makefile&lt;/code&gt; still works through a backward-compat shim, but it nags you with a deprecation warning on every build and points you at &lt;code&gt;flat.mk&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Pandoc went from 2.18 to 3.9.0.2. That brings MathJax to MathML, stricter raw-HTML handling, and changed citeproc behavior. Rendering can move under you, so this is the part to actually look at rather than assume.
&lt;/p&gt;

&lt;p&gt;
And a few behaviors shifted. Bare &lt;code&gt;make&lt;/code&gt; now builds HTML, LaTeX, &lt;i&gt;and&lt;/i&gt; PDF; it used to build only PDF. &lt;code&gt;make clean&lt;/code&gt; now removes only the output directory, and wiping the downloaded toolchain is &lt;code&gt;make distclean&lt;/code&gt;. The generated stable-reference file was renamed &lt;code&gt;srefs.md&lt;/code&gt; to &lt;code&gt;srefs.defs&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org12ed522" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org12ed522"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; Where I started, and why it was a mess&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
The layout was an ordinary flat one.
&lt;/p&gt;

&lt;pre class="example" id="org5fdfda6"&gt;
transcode/
`-- papers/
    |-- Makefile           # one line: include wg21/Makefile
    |-- transcode-view.md  # the actual paper
    |-- TEST.md
    `-- wg21/              # the framework, as a git subtree
&lt;/pre&gt;

&lt;p&gt;
The trouble was all hiding inside &lt;code&gt;papers/wg21/&lt;/code&gt;. A subtree vendors the framework's files directly into your tree, which is the whole point–and also the whole problem. Anything that edits files in your repo edits the framework too, and you won't necessarily notice.
&lt;/p&gt;

&lt;p&gt;
So two things had happened. My &lt;code&gt;pre-commit&lt;/code&gt; config didn't exclude the subtree, so &lt;code&gt;ruff&lt;/code&gt; and friends had quietly reformatted &lt;code&gt;wg21.py&lt;/code&gt; (489 lines of pure churn), &lt;code&gt;refs.py&lt;/code&gt;, and others. None of it changed behavior. All of it was a future merge conflict. On top of that I had genuine local patches for a corporate MITM-TLS network: a CA-bundle export, &lt;code&gt;verify=False&lt;/code&gt; in &lt;code&gt;refs.py&lt;/code&gt; where it fetches &lt;code&gt;wg21.link&lt;/code&gt;, a switch from &lt;code&gt;pip&lt;/code&gt; to &lt;code&gt;uv&lt;/code&gt;, and a &lt;code&gt;truststore&lt;/code&gt; dependency.
&lt;/p&gt;

&lt;p&gt;
One pile I wanted to throw away. The other I needed to keep. Neither belonged in the subtree, which is the lesson I'll keep repeating: the vendored copy should be a verbatim mirror of upstream and nothing else.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgb20e5c2" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgb20e5c2"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; Branch, and a baseline to diff against&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;git switch -c chore/update-wg21-subtree

&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;Build the current (Pandoc 2.18) output, to diff against later.
&lt;/span&gt;&lt;span class="org-builtin"&gt;cd&lt;/span&gt; papers &amp;amp;&amp;amp; make transcode-view.html
cp generated/transcode-view.html /tmp/baseline.html
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Crossing a Pandoc major version, I want to &lt;i&gt;see&lt;/i&gt; what moved, not trust that nothing did. The baseline costs a couple of minutes and pays for itself.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgc64f35a" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgc64f35a"&gt;&lt;span class="section-number-2"&gt;4.&lt;/span&gt; Refreshing the subtree&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;p&gt;
&lt;code&gt;git subtree pull&lt;/code&gt; refuses to run with a dirty tree, so stash anything unrelated first, then pull from upstream master, squashed, the same way the subtree was originally adopted.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;git stash push -m park-unrelated -- some/unrelated/file
git subtree pull --prefix=papers/wg21 &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    https://github.com/mpark/wg21.git master --squash &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    -m &lt;span class="org-string"&gt;"Update wg21 subtree to mpark/master (Pandoc 3.9, Make overhaul)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Because my subtree had diverged, this conflicted, exactly as it should. Six files collided on content, and two were modify/delete conflicts where upstream had deleted files I'd touched. My goal was simple, so the resolution was simple: the subtree should end up identical to upstream master. Take theirs, everywhere.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;Take upstream ("theirs") for the content conflicts, then stage them.
&lt;/span&gt;git checkout --theirs -- papers/wg21/Makefile &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    papers/wg21/data/filters/citetitle.py &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    papers/wg21/data/filters/wg21.py &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    papers/wg21/data/templates/wg21.latex.patch &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    papers/wg21/deps/install-pandoc.sh &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    papers/wg21/deps/requirements.txt
git add papers/wg21/...   &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;the files above
&lt;/span&gt;
&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;Upstream deleted these; accept the deletion.
&lt;/span&gt;git rm -f papers/wg21/generated/TEST.html papers/wg21/tools/TEST-side-by-side.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgef0cd98" class="ox-nikola-outline-3"&gt;
&lt;h3 id="orgef0cd98"&gt;&lt;span class="section-number-3"&gt;4.1.&lt;/span&gt; The merge conflict you don't get&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-4-1"&gt;
&lt;p&gt;
Here is the part that a casual "resolve and commit" walks straight past. A three-way merge only raises a conflict when &lt;i&gt;both&lt;/i&gt; sides changed the same file. For a file I had churned but upstream hadn't touched since the squash base, Git auto-merges by silently keeping my version. No conflict. No prompt. The reformatted file just survives.
&lt;/p&gt;

&lt;p&gt;
The conflict it doesn't raise is the one that bites.
&lt;/p&gt;

&lt;p&gt;
So don't trust the conflict list. Diff the whole subtree against upstream and make sure it's byte-for-byte.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;"mode sha path" for the staged subtree, prefix stripped:
&lt;/span&gt;git ls-files -s papers/wg21 &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
  | awk &lt;span class="org-string"&gt;'{p=$4; sub(/^papers\/wg21\//,"",p); print $1, $2, p}'&lt;/span&gt; | sort &amp;gt; /tmp/staged.txt

&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;The same listing from a checkout of upstream master:
&lt;/span&gt;git -C /path/to/mpark-wg21-checkout ls-tree -r master &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
  | awk &lt;span class="org-string"&gt;'{print $1, $3, $4}'&lt;/span&gt; | sort &amp;gt; /tmp/upstream.txt

diff /tmp/staged.txt /tmp/upstream.txt &amp;amp;&amp;amp; &lt;span class="org-builtin"&gt;echo&lt;/span&gt; &lt;span class="org-string"&gt;"IDENTICAL TO UPSTREAM"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This caught two files--&lt;code&gt;refs.py&lt;/code&gt; and &lt;code&gt;toc-depth.py&lt;/code&gt;–still carrying their reformatted selves. I overwrote them with the upstream copies, re-ran the diff until it was empty, and only then committed the merge.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;cp /path/to/mpark-wg21-checkout/data/refs.py      papers/wg21/data/refs.py
cp /path/to/mpark-wg21-checkout/data/toc-depth.py papers/wg21/data/toc-depth.py
git add papers/wg21/data/refs.py papers/wg21/data/toc-depth.py
&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;diff is empty -&amp;gt; commit the in-progress subtree merge
&lt;/span&gt;git commit --no-verify
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The squashed merge keeps the subtree metadata intact, so the next refresh is just another &lt;code&gt;git subtree pull --squash&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgba442ff" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgba442ff"&gt;&lt;span class="section-number-2"&gt;5.&lt;/span&gt; One line to &lt;code&gt;flat.mk&lt;/code&gt;&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-5"&gt;
&lt;p&gt;
The migration proper is a single line in &lt;i&gt;my&lt;/i&gt; Makefile, not the subtree's.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-makefile"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;include&lt;/span&gt; &lt;span class="org-variable-name"&gt;wg21/flat.mk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
While I was in there I folded the environment handling into the same file, because &lt;code&gt;papers/Makefile&lt;/code&gt; is mine to edit and the subtree is not.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org10489a2" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org10489a2"&gt;&lt;span class="section-number-2"&gt;6.&lt;/span&gt; Keeping the subtree pristine&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-6"&gt;
&lt;p&gt;
This is the whole philosophy, so I'll say it plainly: never patch a file inside the subtree. Every line you change there is a line you re-fight on every pull. Both kinds of drift get pushed back out.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org1b857d7" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org1b857d7"&gt;&lt;span class="section-number-3"&gt;6.1.&lt;/span&gt; Environment patches go in my Makefile&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-6-1"&gt;
&lt;p&gt;
The framework fetches &lt;code&gt;wg21.link&lt;/code&gt; with Python &lt;code&gt;requests&lt;/code&gt; and builds a venv with &lt;code&gt;pip&lt;/code&gt;. On a TLS-intercepting network both have to trust the intercepting root CA. Instead of editing &lt;code&gt;refs.py&lt;/code&gt; to pass &lt;code&gt;verify=False&lt;/code&gt;, or swapping &lt;code&gt;uv&lt;/code&gt; into the install script, I point everything at the system CA bundle from &lt;code&gt;papers/Makefile&lt;/code&gt;.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-makefile"&gt;&lt;code&gt;&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;papers/Makefile
&lt;/span&gt;&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;Point Python / requests / pip at the system CA bundle so the wg21.link fetch
&lt;/span&gt;&lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;and the venv install validate certs, without touching the subtree.
&lt;/span&gt;export &lt;span class="org-variable-name"&gt;SSL_CERT_FILE&lt;/span&gt;      := /etc/ssl/certs/ca-certificates.crt
export &lt;span class="org-variable-name"&gt;REQUESTS_CA_BUNDLE&lt;/span&gt; := /etc/ssl/certs/ca-certificates.crt
export &lt;span class="org-variable-name"&gt;PIP_CERT&lt;/span&gt;           := /etc/ssl/certs/ca-certificates.crt

&lt;span class="org-keyword"&gt;include&lt;/span&gt; &lt;span class="org-variable-name"&gt;wg21/flat.mk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The reward is that the stock upstream &lt;code&gt;refs.py&lt;/code&gt; now fetches &lt;code&gt;wg21.link&lt;/code&gt; with verification passing–no &lt;code&gt;verify=False&lt;/code&gt;, no &lt;code&gt;InsecureRequestWarning&lt;/code&gt;–and the venv builds with plain &lt;code&gt;pip&lt;/code&gt;. The &lt;code&gt;uv&lt;/code&gt; and &lt;code&gt;truststore&lt;/code&gt; patches turned out to be unnecessary once the certificates simply validated. &lt;code&gt;uv&lt;/code&gt; is genuinely nice for speed; I'll propose a clean &lt;code&gt;PIP&lt;/code&gt; override upstream rather than fork the install script to get it back.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org96e0659" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org96e0659"&gt;&lt;span class="section-number-3"&gt;6.2.&lt;/span&gt; Churn gets fenced out of pre-commit&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-6-2"&gt;
&lt;p&gt;
To stop my hooks reformatting the subtree forever, I excluded it.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-yaml"&gt;&lt;code&gt;# .pre-commit-config.yaml
# papers/wg21/ is mpark/wg21 vendored as a git subtree; keep it pristine so
# `git subtree pull` stays conflict-free.
exclude: 'template/|copier/|infra/|port/|papers/wg21/'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orga64ed4e" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orga64ed4e"&gt;&lt;span class="section-number-2"&gt;7.&lt;/span&gt; Verifying, because Pandoc moved a major version&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-7"&gt;
&lt;p&gt;
I built every format and compared against the baseline.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;span class="org-builtin"&gt;cd&lt;/span&gt; papers
make transcode-view.html
make transcode-view.latex transcode-view.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The things I actually looked at: comparison tables (&lt;code&gt;::: cmptable&lt;/code&gt;) still render; math is MathML rather than MathJax; the &lt;code&gt;&amp;lt;!-- markdownlint --&amp;gt;&lt;/code&gt; line stays an invisible HTML comment instead of leaking as visible text; the title block and citations look right.
&lt;/p&gt;

&lt;p&gt;
The build also surfaced three problems that were already there in the 2.18 baseline, so not the upgrade's fault, but worth knowing. Seven citations defined in my local &lt;code&gt;.bib&lt;/code&gt; files don't resolve, because the framework's bibliography configuration wins over the document's own &lt;code&gt;bibliography:&lt;/code&gt; key. A duplicate &lt;code&gt;email:&lt;/code&gt; key in the paper's YAML means Pandoc 3.x keeps only the last one. And emoji and superscript glyphs aren't in the default LaTeX font, so they quietly vanish from the PDF. Bugs for another day, but I'd rather know about them.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgedf30cf" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgedf30cf"&gt;&lt;span class="section-number-2"&gt;8.&lt;/span&gt; Living with it&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-8"&gt;
&lt;p&gt;
The flat layout is pleasant once it's in place. From &lt;code&gt;papers/&lt;/code&gt;:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;make transcode-view.html    &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;-&amp;gt; generated/transcode-view.html
&lt;/span&gt;make transcode-view.pdf     &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;-&amp;gt; generated/transcode-view.pdf
&lt;/span&gt;make transcode-view.latex   &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;the intermediate LaTeX
&lt;/span&gt;
make                        &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;ALL papers, ALL formats (html + latex + pdf)
&lt;/span&gt;make html                   &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;all papers, HTML only
&lt;/span&gt;make clean                  &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;remove generated/ (NOT the toolchain)
&lt;/span&gt;make distclean              &lt;span class="org-comment-delimiter"&gt;# &lt;/span&gt;&lt;span class="org-comment"&gt;also remove the downloaded Pandoc + venv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Two things to remember. Bare &lt;code&gt;make&lt;/code&gt; now builds three formats, not one, so name the target if CI only wants HTML. And the downloaded Pandoc and the venv live under &lt;code&gt;papers/wg21/deps/&lt;/code&gt; and are git-ignored; the first build re-fetches them, and &lt;code&gt;make distclean&lt;/code&gt; gets the space back.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org5c3ab7b" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org5c3ab7b"&gt;&lt;span class="section-number-2"&gt;9.&lt;/span&gt; Refreshing, next time&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-9"&gt;
&lt;p&gt;
Because the subtree is pristine and fenced off from the hooks, the next update is the one-liner the tool always promised.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;git subtree pull --prefix=papers/wg21 &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
    https://github.com/mpark/wg21.git master --squash
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If it ever conflicts again, the recipe is the same: resolve toward upstream, then run the &lt;code&gt;ls-files&lt;/code&gt; versus &lt;code&gt;ls-tree&lt;/code&gt; diff to prove the subtree is byte-identical before committing.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgbc7f2f9" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgbc7f2f9"&gt;&lt;span class="section-number-2"&gt;10.&lt;/span&gt; What I'd tell myself&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-10"&gt;
&lt;p&gt;
Subtree versus submodule is a real choice, not a coin flip. A subtree vendors files into your tree, which means your repo-wide tooling will happily edit the vendored code. Decide that on purpose and fence it off.
&lt;/p&gt;

&lt;p&gt;
Patches belong downstream. Anything you can say in your own Makefile or config–an environment variable, an include, an exclude–should live there, not inside the vendored copy.
&lt;/p&gt;

&lt;p&gt;
And the merge that silently keeps your side is the dangerous one, not the noisy conflict you can see. After any subtree refresh, diff against upstream. Trust the diff, not the conflict list.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/migrating-wg21-subtree/</guid><pubDate>Mon, 29 Jun 2026 03:37:51 GMT</pubDate></item><item><title>Moving Forward With Legacy Encodings</title><link>https://sdowney.org/posts/moving-forward-with-legacy-encodings/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div id="ox-nikola-outline-container-org094ca86" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org094ca86"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; Abstract&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
Reverse-parsing legacy multibyte text encodings — such as Shift_JIS, Big5, or GB18030 — using only local context is an unsolvable problem. Unlike UTF-8, which guarantees \(O(1)\) self-synchronization, legacy encodings have heavily overlapping lead and trail byte ranges. Consequently, even if you begin at a known, valid character boundary, computing the byte-width of the preceding character requires an \(O(N)\) backward scan to the beginning of the string to resolve the parity of the sequence.
&lt;/p&gt;

&lt;p&gt;
The WHATWG decoding algorithms provide no mitigation, as their forward-looking state machines reset completely at every boundary. Robust reverse iteration through these encodings cannot be solved algorithmically &lt;i&gt;in situ&lt;/i&gt;; it requires maintaining an external cache of boundary offsets established during a forward pass.
&lt;/p&gt;

&lt;p&gt;
What follows is the story of attempting to find a way out, and why the math forces us to fail.
&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org3408eff" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org3408eff"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; The Story of Failing to Find a Way Out&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org9ac8563" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org9ac8563"&gt;&lt;span class="section-number-3"&gt;2.1.&lt;/span&gt; Act I: The Overlapping Abyss&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-1"&gt;
&lt;p&gt;
If you are dropped into the middle of an arbitrary point in a string using a WHATWG legacy multibyte encoding, you cannot find the beginning of a character just by reading backward.
&lt;/p&gt;

&lt;p&gt;
This happens because legacy encodings lack &lt;i&gt;self-synchronization&lt;/i&gt;. Let \(L\) be the set of valid lead bytes and \(T\) be the set of valid trail bytes. In UTF-8, these sets are strictly disjoint (\(L \cap T = \emptyset\)). In legacy encodings, the sets intersect aggressively. In Shift_JIS, the byte &lt;code&gt;0x81&lt;/code&gt; is valid as &lt;i&gt;both&lt;/i&gt; a lead byte and a trail byte.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org589fd68" class="ox-nikola-outline-4"&gt;
&lt;h4 id="org589fd68"&gt;&lt;span class="section-number-4"&gt;2.1.1.&lt;/span&gt; The Specific Example&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-1-1"&gt;
&lt;p&gt;
Imagine you are handed a data buffer and dropped at index 3:
&lt;/p&gt;

&lt;pre class="example" id="orgdfc3366"&gt;
Buffer: [0x81, 0x81, 0x81, 0x81, 0x81]
Index:    0     1     2     3     4
&lt;/pre&gt;

&lt;p&gt;
If you look locally at index 3 (which is &lt;code&gt;0x81&lt;/code&gt;), is it the start of a character or the second half of one? You cannot know without scanning backward to the true beginning of the string:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;b&gt;If the string actually started at index 1:&lt;/b&gt; The parsing goes &lt;code&gt;[0x81 0x81]&lt;/code&gt; (Char 1) and &lt;code&gt;[0x81 0x81]&lt;/code&gt; (Char 2). Your byte at index 3 is a &lt;b&gt;lead byte&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;If the string actually started at index 0:&lt;/b&gt; The parsing goes &lt;code&gt;[0x81 0x81]&lt;/code&gt; (Char 1), &lt;code&gt;[0x81 0x81]&lt;/code&gt; (Char 2), leaving index 4 orphaned. Your byte at index 3 is a &lt;b&gt;trail byte&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Every single byte relies entirely on the even/odd parity established by the byte preceding it. If the string is malformed or sliced, the ambiguity is even worse, as dropped trail bytes leave no algorithmic breadcrumbs going backward.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org86485f3" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org86485f3"&gt;&lt;span class="section-number-3"&gt;2.2.&lt;/span&gt; Act II: The Illusion of the Anchor&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-2"&gt;
&lt;p&gt;
Suppose your position is not random. You just parsed forward from the beginning of the string, so you know for an absolute fact you are sitting exactly at a valid character boundary.
&lt;/p&gt;

&lt;p&gt;
You still cannot reliably step backward. Knowing the &lt;i&gt;end&lt;/i&gt; of the previous character does not tell you its &lt;i&gt;length&lt;/i&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org12988a4" class="ox-nikola-outline-4"&gt;
&lt;h4 id="org12988a4"&gt;&lt;span class="section-number-4"&gt;2.2.1.&lt;/span&gt; The Specific Example&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-2-1"&gt;
&lt;p&gt;
Imagine looking backward from your known, forward-parsed cursor and seeing this sequence in Shift_JIS:
&lt;/p&gt;

&lt;pre class="example" id="orge5ebb59"&gt;
... 0x81 0x81 0x81 0x40 | &amp;lt;CURSOR&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
We know &lt;code&gt;0x40&lt;/code&gt; is ASCII &lt;code&gt;@&lt;/code&gt; (a valid 1-byte character) AND a valid trail byte. We know &lt;code&gt;0x81&lt;/code&gt; is a lead byte AND a trail byte. Where does the character &lt;i&gt;before&lt;/i&gt; your cursor begin?
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;b&gt;Scenario A (Odd number of preceding ~0x81~s):&lt;/b&gt; The sequence from the start of the string was &lt;code&gt;[0x81 0x81]&lt;/code&gt; (U+3001 Ideographic Comma) followed by &lt;code&gt;[0x81 0x40]&lt;/code&gt; (U+3000 Ideographic Space).
\(\rightarrow\) &lt;i&gt;Result:&lt;/i&gt; The character before your cursor is &lt;b&gt;2 bytes long&lt;/b&gt;, and you just parsed a Japanese space.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Scenario B (Even number of preceding ~0x81~s):&lt;/b&gt; The sequence from the start of the string was &lt;code&gt;[0x81 0x81]&lt;/code&gt; (U+3001), &lt;code&gt;[0x81 0x81]&lt;/code&gt; (U+3001), followed by a standalone &lt;code&gt;[0x40]&lt;/code&gt; (U+0040 '@').
\(\rightarrow\) &lt;i&gt;Result:&lt;/i&gt; The character before your cursor is &lt;b&gt;1 byte long&lt;/b&gt;, and you just parsed an English '@' symbol.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Even though you are sitting at a known boundary, you are forced into the exact same backward dependency chain to resolve the parity of the preceding bytes just to know if you should step back 1 byte or 2 bytes.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org9466f2a" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org9466f2a"&gt;&lt;span class="section-number-3"&gt;2.3.&lt;/span&gt; Act III: State Machine Amnesia&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-3"&gt;
&lt;p&gt;
Finally, you might think to capture the exact WHATWG parser state at your cursor position, rather than just the pointer.
&lt;/p&gt;

&lt;p&gt;
This is the final dead end. Having the parser state provides absolutely zero historical context because the WHATWG state machine suffers from "amnesia" at character boundaries. Decoders operate as forward-looking Mealy machines. When you reach a valid character boundary, the decoder finishes emitting the Unicode scalar value and immediately resets its internal state to the &lt;i&gt;Default&lt;/i&gt; state (waiting for a new ASCII character or a new multibyte lead byte).
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org1081165" class="ox-nikola-outline-4"&gt;
&lt;h4 id="org1081165"&gt;&lt;span class="section-number-4"&gt;2.3.1.&lt;/span&gt; The Specific Example&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-3-1"&gt;
&lt;p&gt;
Let's track the exact parser state for the end of the two scenarios from Act II to see why it fails to help us read backwards.
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;b&gt;Trace A (The preceding character was '@'):&lt;/b&gt;
&lt;ol class="org-ol"&gt;
&lt;li&gt;Parser reads &lt;code&gt;0x81&lt;/code&gt; \(\rightarrow\) State changes to: &lt;i&gt;Shift_JIS lead&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Parser reads &lt;code&gt;0x81&lt;/code&gt; (Emits U+3001) \(\rightarrow\) State resets to: &lt;i&gt;Default&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Parser reads &lt;code&gt;0x40&lt;/code&gt; (Emits U+0040) \(\rightarrow\) State resets to: &lt;b&gt;Default&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Trace B (The preceding character was Ideographic Space):&lt;/b&gt;
&lt;ol class="org-ol"&gt;
&lt;li&gt;Parser reads &lt;code&gt;0x81&lt;/code&gt; \(\rightarrow\) State changes to: &lt;i&gt;Shift_JIS lead&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Parser reads &lt;code&gt;0x40&lt;/code&gt; (Emits U+3000) \(\rightarrow\) State resets to: &lt;b&gt;Default&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
In both traces, you are left at the exact same byte position, looking at the exact same bytes behind you, and your parser state is completely identical: &lt;b&gt;Default&lt;/b&gt;. The parser state only tells you what to do with the &lt;i&gt;next&lt;/i&gt; byte you encounter.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org7feda23" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org7feda23"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; Formal Definitions&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org1fd066b" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org1fd066b"&gt;&lt;span class="section-number-3"&gt;3.1.&lt;/span&gt; Self-Synchronizing Code&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-3-1"&gt;
&lt;p&gt;
A code is self-synchronizing if, given an arbitrary point in a bit stream, the decoder can determine the boundaries of the next encoded sequence within a bounded number of bits (\(O(1)\) scan). UTF-8 achieves this via distinct prefix bit-patterns (e.g., &lt;code&gt;110xxxxx&lt;/code&gt; for a 2-byte lead, &lt;code&gt;10xxxxxx&lt;/code&gt; for a trail).
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org8c5cb33" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org8c5cb33"&gt;&lt;span class="section-number-3"&gt;3.2.&lt;/span&gt; Decoder State Amnesia&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-3-2"&gt;
&lt;p&gt;
The property of WHATWG decoders where the internal state matrix transitions to the origin state upon the emission of any complete scalar value. Because the state maps purely to &lt;i&gt;pending&lt;/i&gt; bytes, no history of &lt;i&gt;completed&lt;/i&gt; byte widths is preserved.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgc72c8f8" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgc72c8f8"&gt;&lt;span class="section-number-2"&gt;4.&lt;/span&gt; Tables and Proofs (For the Unbelieving)&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;p&gt;
If you still suspect there might be a clever bitwise trick to differentiate a lead byte from a trail byte in Shift_JIS, observe the exact byte ranges defined by the standard.
&lt;/p&gt;

&lt;p&gt;
The overlap is not a minor edge case; it is systemic. Every single valid lead byte is also a valid trail byte.
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope="col" class="org-left"&gt;Byte Type&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Allowed Hexadecimal Ranges&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;b&gt;Lead&lt;/b&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;&lt;code&gt;[0x81, 0x9F]&lt;/code&gt; \(\cup\) &lt;code&gt;[0xE0, 0xFC]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;b&gt;Trail&lt;/b&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;&lt;code&gt;[0x40, 0x7E]&lt;/code&gt; \(\cup\) &lt;code&gt;[0x80, 0xFC]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
If we compute the intersection (\(Lead \cap Trail\)), we find that the &lt;i&gt;entirety&lt;/i&gt; of the lead byte range is subsumed by the trail byte range.
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope="col" class="org-left"&gt;Condition&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Hexadecimal Range&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Consequence for Reverse Parsing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;\(B \in L \land B \notin T\)&lt;/td&gt;
&lt;td class="org-left"&gt;\(\emptyset\)&lt;/td&gt;
&lt;td class="org-left"&gt;No byte unambiguously marks the start of a multibyte character.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;\(B \in T \land B \notin L\)&lt;/td&gt;
&lt;td class="org-left"&gt;&lt;code&gt;[0x40, 0x7E]&lt;/code&gt; \(\cup\) &lt;code&gt;0x80&lt;/code&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;Can be an ASCII character &lt;i&gt;or&lt;/i&gt; a trail byte.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;\(B \in (L \cap T)\)&lt;/td&gt;
&lt;td class="org-left"&gt;&lt;code&gt;[0x81, 0x9F]&lt;/code&gt; \(\cup\) &lt;code&gt;[0xE0, 0xFC]&lt;/code&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;Utter ambiguity. Requires \(O(N)\) lookbehind.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgbae6495" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgbae6495"&gt;&lt;span class="section-number-2"&gt;5.&lt;/span&gt; References&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-5"&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;b&gt;WHATWG Encoding Standard, Section 4 (Decoders):&lt;/b&gt; Explicitly defines the forward-looking architecture of legacy decoders and the mandatory &lt;i&gt;Shift_JIS lead&lt;/i&gt; to &lt;i&gt;Default&lt;/i&gt; state transitions. &lt;a href="https://encoding.spec.whatwg.org/#decoders"&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WHATWG Encoding Standard, Section 13 (Shift_JIS):&lt;/b&gt; Outlines the index pointer maps showing the overlapping lead and trail ranges. &lt;a href="https://encoding.spec.whatwg.org/#shift_jis"&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;The Unicode Standard, Version 15.0, Section 2.5 (Encoding Forms):&lt;/b&gt; Discusses the architectural necessity of non-overlapping (self-synchronizing) code unit boundaries, contrasting UTF-8 with legacy designs. &lt;a href="https://www.unicode.org/versions/Unicode15.0.0/ch02.pdf"&gt;Link&lt;/a&gt;Write your post here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/moving-forward-with-legacy-encodings/</guid><pubDate>Sun, 24 May 2026 17:11:21 GMT</pubDate></item><item><title>Tailwind, Modus Themes, and the Blog Theming Workflow</title><link>https://sdowney.org/posts/tailwind-theme-workflow/</link><dc:creator>Steve Downey</dc:creator><description>&lt;p&gt;
I replaced the Foundation 6 theme on this blog with Tailwind CSS.  The
immediate motivation was a CSS conflict—Foundation's global &lt;code&gt;code&lt;/code&gt; and &lt;code&gt;kbd&lt;/code&gt;
rules bled into org-mode source blocks—but the deeper reason is that Tailwind
has the community and documentation that Foundation no longer does.
&lt;/p&gt;

&lt;p&gt;
This post documents the workflow: how the theme is structured, how syntax
highlighting CSS connects Emacs to the browser, and what the current
configuration choices are.
&lt;/p&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-org99d35f8" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org99d35f8"&gt;The Theme Stack&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org99d35f8"&gt;
&lt;p&gt;
The blog uses three Nikola themes stacked via the parent chain:
&lt;/p&gt;

&lt;pre class="example" id="org7f89ea4"&gt;
sdowney-tailwind → nikola-tailwind-blog → nikola-tailwind-base → base
&lt;/pre&gt;

&lt;p&gt;
&lt;code&gt;nikola-tailwind-base&lt;/code&gt; provides the Tailwind Play CDN loading, a responsive
navbar, a centered content column wrapped in Tailwind's &lt;code&gt;prose&lt;/code&gt; class, and CSS
for org-mode source blocks (centering, language labels, copy buttons).  It has
no opinions about fonts or accent colors.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;nikola-tailwind-blog&lt;/code&gt; adds an indigo accent, post/index/tag templates, and the
visual structure of a blog.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;sdowney-tailwind&lt;/code&gt; is just my personal layer: Google Fonts (Source Sans 3, Open
Sans, Source Code Pro), Font Awesome, and the modus-vivendi syntax highlighting
CSS.
&lt;/p&gt;

&lt;p&gt;
Each tier is a standard Nikola theme directory under &lt;code&gt;themes/&lt;/code&gt;.  Nikola finds
templates by walking the chain, so I only override what I actually change.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org700aeb0" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org700aeb0"&gt;How Tailwind Is Loaded&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org700aeb0"&gt;
&lt;p&gt;
The Tailwind Play CDN is a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag that processes utility classes at
runtime:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-html"&gt;&lt;code&gt;&amp;lt;&lt;span class="org-function-name"&gt;script&lt;/span&gt; &lt;span class="org-variable-name"&gt;src&lt;/span&gt;=&lt;span class="org-string"&gt;"https://cdn.tailwindcss.com?plugins=typography"&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span class="org-function-name"&gt;script&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The Typography plugin provides the &lt;code&gt;prose&lt;/code&gt; class, which styles all content
elements (headings, paragraphs, lists, blockquotes, code, tables) inside the
main content area.  For a blog where content comes from org-mode export, this
is exactly right—I don't control the HTML org produces, and &lt;code&gt;prose&lt;/code&gt; handles
it gracefully.
&lt;/p&gt;

&lt;p&gt;
The Tailwind config is inline in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block, defined in my personal
&lt;code&gt;base_helper.tmpl&lt;/code&gt;:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-javascript"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;var&lt;/span&gt; &lt;span class="org-variable-name"&gt;heading&lt;/span&gt; = [&lt;span class="org-string"&gt;'"Open Sans"'&lt;/span&gt;, &lt;span class="org-string"&gt;'Roboto'&lt;/span&gt;, &lt;span class="org-string"&gt;'Arial'&lt;/span&gt;, &lt;span class="org-string"&gt;'sans-serif'&lt;/span&gt;];
tailwind.config = {
    theme: {
        extend: {
            fontFamily: {
                sans: [&lt;span class="org-string"&gt;'"Source Sans 3"'&lt;/span&gt;].concat(heading),
                heading: heading,
                mono: [&lt;span class="org-string"&gt;'"Source Code Pro"'&lt;/span&gt;, &lt;span class="org-string"&gt;'Inconsolata'&lt;/span&gt;, &lt;span class="org-string"&gt;'ui-monospace'&lt;/span&gt;, &lt;span class="org-string"&gt;'Courier'&lt;/span&gt;, &lt;span class="org-string"&gt;'monospace'&lt;/span&gt;],
            },
            typography: {
                DEFAULT: {
                    css: {
                        &lt;span class="org-string"&gt;'h1, h2, h3, h4, h5, h6'&lt;/span&gt;: {
                            fontFamily: heading.join(&lt;span class="org-string"&gt;', '&lt;/span&gt;),
                        },
                    },
                },
            },
        },
    },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Font stacks are defined once.  The &lt;code&gt;heading&lt;/code&gt; array is reused in three places.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orga7e8c22" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orga7e8c22"&gt;Source Block Rendering&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-orga7e8c22"&gt;
&lt;p&gt;
Org-mode's HTML exporter produces this structure:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-html"&gt;&lt;code&gt;&amp;lt;&lt;span class="org-function-name"&gt;div&lt;/span&gt; &lt;span class="org-variable-name"&gt;class&lt;/span&gt;=&lt;span class="org-string"&gt;"org-src-container"&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span class="org-function-name"&gt;pre&lt;/span&gt; &lt;span class="org-variable-name"&gt;class&lt;/span&gt;=&lt;span class="org-string"&gt;"src src-cpp"&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="org-function-name"&gt;code&lt;/span&gt;&amp;gt;&amp;lt;&lt;span class="org-function-name"&gt;span&lt;/span&gt; &lt;span class="org-variable-name"&gt;class&lt;/span&gt;=&lt;span class="org-string"&gt;"org-keyword"&lt;/span&gt;&amp;gt;template&amp;lt;/&lt;span class="org-function-name"&gt;span&lt;/span&gt;&amp;gt; ...&amp;lt;/&lt;span class="org-function-name"&gt;code&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span class="org-function-name"&gt;pre&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span class="org-function-name"&gt;div&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;src-cpp&lt;/code&gt; class carries the language.  JavaScript in &lt;code&gt;base.tmpl&lt;/code&gt; reads it,
maps &lt;code&gt;cpp&lt;/code&gt; to "C++", and injects a label in the top-left corner and a copy
button in the top-right.  Any language works—unknown ones display their raw
name.
&lt;/p&gt;

&lt;p&gt;
The CSS positions the container with &lt;code&gt;position: relative&lt;/code&gt;, &lt;code&gt;margin: auto&lt;/code&gt;,
&lt;code&gt;width: fit-content&lt;/code&gt;, &lt;code&gt;min-width: 60%&lt;/code&gt;, and gives the &lt;code&gt;pre&lt;/code&gt; a dark background
(&lt;code&gt;--code-bg&lt;/code&gt;) with padding.  The &lt;code&gt;padding-top: 2.25rem&lt;/code&gt; makes room for the
label and button.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org54cbb7b" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org54cbb7b"&gt;Syntax Highlighting: The Modus Connection&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org54cbb7b"&gt;
&lt;p&gt;
Org-mode's HTML exporter uses &lt;code&gt;htmlize&lt;/code&gt; to apply face colors as CSS classes.
When &lt;code&gt;org-html-htmlize-output-type&lt;/code&gt; is &lt;code&gt;'css&lt;/code&gt;, it produces &lt;code&gt;&amp;lt;span
class&lt;/code&gt;"org-keyword"&amp;gt;=, &lt;code&gt;&amp;lt;span class&lt;/code&gt;"org-type"&amp;gt;=, etc.  The corresponding CSS
must exist in an external stylesheet.
&lt;/p&gt;

&lt;p&gt;
Protesilaos Stavrou's &lt;a href="https://protesilaos.com/emacs/modus-themes"&gt;modus-themes&lt;/a&gt; provide a structured palette: an alist of
&lt;code&gt;(semantic-name . hex-color)&lt;/code&gt; pairs.  Face definitions reference these palette
names.  &lt;code&gt;font-lock-keyword-face&lt;/code&gt;, for example, maps to &lt;code&gt;magenta-alt-other&lt;/code&gt; in
modus-vivendi.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;secretaire-css.el&lt;/code&gt; bridges Emacs and the browser:
&lt;/p&gt;

&lt;ol class="org-ol"&gt;
&lt;li&gt;Load the target theme (e.g., &lt;code&gt;modus-vivendi-tinted&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Require deferred major modes so their faces exist&lt;/li&gt;
&lt;li&gt;Read the palette via &lt;code&gt;(modus-themes-current-palette)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;For each face, resolve attributes and reverse-map hex → palette name&lt;/li&gt;
&lt;li&gt;Emit CSS rules using &lt;code&gt;var(--palette-name)&lt;/code&gt; references&lt;/li&gt;
&lt;li&gt;Include only referenced palette entries in the &lt;code&gt;:root&lt;/code&gt; block&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
The result:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-css"&gt;&lt;code&gt;&lt;span class="org-css-selector"&gt;:root&lt;/span&gt; {
    &lt;span class="org-variable-name"&gt;--bg-main&lt;/span&gt;: &lt;span class="custom-3"&gt;#0d0e1c&lt;/span&gt;;
    &lt;span class="org-variable-name"&gt;--fg-main&lt;/span&gt;: &lt;span class="custom-2"&gt;#ffffff&lt;/span&gt;;
    &lt;span class="org-variable-name"&gt;--magenta-alt-other&lt;/span&gt;: &lt;span class="custom-1"&gt;#b6a0ff&lt;/span&gt;;
    &lt;span class="org-variable-name"&gt;--green&lt;/span&gt;: &lt;span class="custom"&gt;#44bc44&lt;/span&gt;;
    &lt;span class="org-comment-delimiter"&gt;/* &lt;/span&gt;&lt;span class="org-comment"&gt;... only used entries ...&lt;/span&gt;&lt;span class="org-comment-delimiter"&gt; */&lt;/span&gt;
}

&lt;span class="org-comment-delimiter"&gt;/* &lt;/span&gt;&lt;span class="org-comment"&gt;font-lock-keyword-face: magenta-alt-other&lt;/span&gt;&lt;span class="org-comment-delimiter"&gt; */&lt;/span&gt;
&lt;span class="org-css-selector"&gt;.org-keyword&lt;/span&gt; {
    &lt;span class="org-css-property"&gt;color&lt;/span&gt;: var(&lt;span class="org-variable-name"&gt;--magenta-alt-other&lt;/span&gt;);
    &lt;span class="org-css-property"&gt;font-weight&lt;/span&gt;: bold;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;tailwind-base.css&lt;/code&gt; file in the base tier references these variables:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-css"&gt;&lt;code&gt;&lt;span class="org-css-selector"&gt;:root&lt;/span&gt; {
    &lt;span class="org-variable-name"&gt;--code-bg&lt;/span&gt;: var(&lt;span class="org-variable-name"&gt;--bg-main&lt;/span&gt;, &lt;span class="custom-1"&gt;#1e1e2e&lt;/span&gt;);
    &lt;span class="org-variable-name"&gt;--code-fg&lt;/span&gt;: var(&lt;span class="org-variable-name"&gt;--fg-main&lt;/span&gt;, &lt;span class="custom"&gt;#cdd6f4&lt;/span&gt;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Change the modus variant, regenerate, and code block backgrounds follow.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgc8052c7" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgc8052c7"&gt;Current Configuration&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-orgc8052c7"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org4363645" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org4363645"&gt;What's using the Play CDN&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org4363645"&gt;
&lt;p&gt;
The Tailwind Play CDN generates CSS client-side.  There is no &lt;code&gt;node_modules&lt;/code&gt;,
no &lt;code&gt;package.json&lt;/code&gt;, no build step.  Page load includes a brief delay while
Tailwind processes the utility classes.  For a personal blog with moderate
traffic, this is acceptable.
&lt;/p&gt;

&lt;p&gt;
If it becomes a problem, the switch to a pre-built CSS file is
straightforward: install the Tailwind CLI, run &lt;code&gt;npx tailwindcss -o
tailwind.css&lt;/code&gt;, replace the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; with a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org0ea1d2a" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org0ea1d2a"&gt;Typography plugin via prose&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org0ea1d2a"&gt;
&lt;p&gt;
The &lt;code&gt;prose prose-lg prose-indigo&lt;/code&gt; classes on the content &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; provide:
&lt;/p&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;Reasonable line lengths and spacing&lt;/li&gt;
&lt;li&gt;Styled headings, links, lists, blockquotes, tables&lt;/li&gt;
&lt;li&gt;Code blocks with monospace font&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prose-indigo&lt;/code&gt; for accent-colored links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Anything outside &lt;code&gt;prose&lt;/code&gt; (navbar, footer, tag pills) uses &lt;code&gt;not-prose&lt;/code&gt; or
lives outside the &lt;code&gt;prose&lt;/code&gt; wrapper.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org5564959" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org5564959"&gt;Fonts&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org5564959"&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;Body: Source Sans 3 (with Open Sans fallback)&lt;/li&gt;
&lt;li&gt;Headings: Open Sans&lt;/li&gt;
&lt;li&gt;Code: Source Code Pro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
All loaded via Google Fonts.  Tailwind's &lt;code&gt;font-sans&lt;/code&gt; and &lt;code&gt;font-mono&lt;/code&gt; utilities
reference these through the config.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org0687fab" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org0687fab"&gt;Accent color: indigo&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org0687fab"&gt;
&lt;p&gt;
Tailwind's &lt;code&gt;indigo&lt;/code&gt; palette provides the accent: &lt;code&gt;bg-indigo-700&lt;/code&gt; for the
header banner, &lt;code&gt;text-indigo-600&lt;/code&gt; for links, &lt;code&gt;bg-indigo-100 text-indigo-700&lt;/code&gt;
for tag pills.  Changing to another color is a mechanical search-replace
across the blog tier templates.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orga55db06" class="ox-nikola-outline-3"&gt;
&lt;h3 id="orga55db06"&gt;Icons: Font Awesome 7.0.1&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-orga55db06"&gt;
&lt;p&gt;
Loaded via CDN in the personal tier.  Navigation links in &lt;code&gt;conf.py&lt;/code&gt; include
Font Awesome &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; tags directly in the link text.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org7b1cb39" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org7b1cb39"&gt;What I Might Tweak Later&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org7b1cb39"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org41ff33b" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org41ff33b"&gt;Dark mode&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org41ff33b"&gt;
&lt;p&gt;
The syntax highlighting is already dark (modus-vivendi).  The page chrome is
light.  A proper dark mode would add &lt;code&gt;dark:&lt;/code&gt; variants to the templates and
switch the modus CSS to operandi for the light code blocks.  Tailwind makes
this straightforward with &lt;code&gt;darkMode: 'media'&lt;/code&gt; in the config.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org7720294" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org7720294"&gt;Production Tailwind&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org7720294"&gt;
&lt;p&gt;
If page load times bother me, switching to a pre-built CSS file removes the
Play CDN runtime cost.  The config is already defined inline; moving it to
&lt;code&gt;tailwind.config.js&lt;/code&gt; is mechanical.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org41c6ffd" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org41c6ffd"&gt;Palette-aware CSS generation&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org41c6ffd"&gt;
&lt;p&gt;
&lt;code&gt;secretaire-css&lt;/code&gt; currently falls back to htmlize for the actual face scanning
when the modus palette API isn't available.  The full palette-aware path (emitting
&lt;code&gt;var()&lt;/code&gt; references instead of flat hex) works with modus-themes 3.x and 4.x.
See &lt;code&gt;docs/secretaire-css-design.org&lt;/code&gt; for what the ideal output looks like.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orge490c0a" class="ox-nikola-outline-3"&gt;
&lt;h3 id="orge490c0a"&gt;Fonts&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-orge490c0a"&gt;
&lt;p&gt;
I'm eyeing &lt;a href="https://www.brailleinstitute.org/freefont/"&gt;Atkinson Hyperlegible Next&lt;/a&gt; for body text.  Changing fonts is a
two-place edit: the Google Fonts &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag and the Tailwind config in
&lt;code&gt;base_helper.tmpl&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>emacs</category><category>modus-themes</category><category>nikola</category><category>org-mode</category><category>tailwind</category><guid>https://sdowney.org/posts/tailwind-theme-workflow/</guid><pubDate>Sun, 10 May 2026 22:00:00 GMT</pubDate></item><item><title>Surround With UUID</title><link>https://sdowney.org/posts/surround-with-uuid/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="orgdcab3ba"&gt;
&lt;p&gt;
The question of why C++ is standardized through ISO comes up fairly often. This is what I came up with as an explanation the last time I tried to answer that.
&lt;/p&gt;

&lt;p&gt;
It's a radically oversimplified too long elevator pitch.
&lt;/p&gt;

&lt;/div&gt;</description><guid>https://sdowney.org/posts/surround-with-uuid/</guid><pubDate>Thu, 09 Apr 2026 21:41:26 GMT</pubDate></item><item><title>Why Standard Organizations</title><link>https://sdowney.org/posts/why-standard-organizations/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="org4eaac25"&gt;
&lt;p&gt;
The question of why C++ is standardized through ISO comes up fairly often. This is what I came up with as an explanation the last time I tried to answer that.
&lt;/p&gt;

&lt;p&gt;
It's a radically oversimplified too long elevator pitch.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;

&lt;p&gt;
"Why Standards Organizations" is actually a really good question, as is the related question of if C and C++ should move from ISO.
&lt;/p&gt;

&lt;p&gt;
Standards organizations exist because standards are important and governments know they aren't particularly competent to create them. But standards can also have legal impact, like the fire code that says building materials must meet some criteria to be legal for people to live in. Screws have to match the tools and the holes they are meant to go in.
&lt;/p&gt;

&lt;p&gt;
The people who have the expertise to define all this are the people who make things. That's actually a problem. Letting BigScrewCo say that BigScrew #7 is the standard means they have a huge advantage. BigScrewCo and BigDriverCo could conspire to cut everyone else out of the market.
&lt;/p&gt;

&lt;p&gt;
So you want an independent organization, with rules to prevent that.
&lt;/p&gt;

&lt;p&gt;
That org gave Microsoft, IBM, and Bell Labs the legal cover to meet with each other and conspire about their competing products. Remember that those companies, in particular, had already agreed to remedies for their antitrust violations. So meeting under the auspices of ANSI made all the lawyers, if not happy, at least less worried.
&lt;/p&gt;

&lt;p&gt;
Open Source did not exist. GNU and the FSF were brand new. Collaborating in public wasn't a thing, either, because we didn't have The Web, even if we did have early Internet.
&lt;/p&gt;

&lt;p&gt;
There are a lot of standards organizations now [insert &lt;a href="https://xkcd.com/927/"&gt;xkcd:Standards&lt;/a&gt;]. Nor does it seem particularly necessary from a legal or technical perspective to host something like a language or ecosystem standard within one. However, existing orgs give some level of credibility.
&lt;/p&gt;

&lt;p&gt;
That's somewhat important, because it short circuits a lot of questions about how good, or what process, etc, for the quality of a standard.
&lt;/p&gt;

&lt;p&gt;
It also helps on the other end, getting participation. It's much easier to explain to my manager and my company that I'm doing work on an ISO, or ECMA, Apache, or Python Software Foundation project and get all the approvals to engage and spend work hours on it. Similarly for GCC or LLVM or a bunch of other established open source projects.
&lt;/p&gt;

&lt;p&gt;
New projects, or my own projects, are possible, but more work. Harder to get approvals, or to spend working hours or actual cash on travel for. And that is similar for everyone everywhere, even to some extend for a sole proprietorship consultancy.
&lt;/p&gt;

&lt;p&gt;
So, yes, while we could set up something entirely new, that makes other things more complicated, and it is more work. And a new organization has to be able to answer the question of why should we listen to &lt;a href="https://github.com/grafikrobot"&gt;René&lt;/a&gt; and Steve, even if &lt;a href="https://robot-dreams.net/author/grafik/"&gt;René&lt;/a&gt; is one of the leading experts on the C++ ecosystem and tools, and &lt;a href="https://avatars.sched.co/8/2f/4704556/avatar.jpg?c22"&gt;Steve&lt;/a&gt; is old and has grey hair, but works for a &lt;a href="https://www.bloomberg.com/company/values/tech-at-bloomberg/"&gt;really big company&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
LLVM and GCC are definitely interested in getting answers to this, especially for the parts that no one ought to care about as long as there is one answer, but neither is really the right group. It also turns out that even walled garden ecosystems end up caring about C, C++, and Fortran, because they want access to the code written in those languages, but integration is a terrible mess.
&lt;/p&gt;

&lt;p&gt;
We joke that the most common build system is not CMake, it's setup.py.
&lt;/p&gt;

&lt;p&gt;
So, a moderate sized post, with a not terribly satisfying engineering answer, because like most hard problems, &lt;a href="https://sdowney.org/pages/quote-file/"&gt;it's not a technical problem it is a people problem.&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
C++ stays at ISO because ISO has rights to the standard, and getting those rights to move it elsewhere is a hard problem, and would make everyone involved have to do some work just to continue to participate. Doing all that would have to be a massively clear win, and so no one is trying to take on the impossible task.
&lt;/p&gt;</description><category>C++</category><guid>https://sdowney.org/posts/why-standard-organizations/</guid><pubDate>Sat, 07 Mar 2026 15:21:36 GMT</pubDate></item><item><title>The Sender Sub-Language</title><link>https://sdowney.org/posts/the-sender-sub-language/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="orgc4d4b42"&gt;
&lt;p&gt;
The paper &lt;a href="http://wg21.link/P4014R0"&gt;The Sender Sub-Language&lt;/a&gt; by Vinnie Falco &amp;lt;vinnie.falco@gmail.com&amp;gt; and Mungo Gill &amp;lt;mungo.gill@me.com&amp;gt; makes extensive use of my work at &lt;a href="https://github.com/steve-downey/sender-examples"&gt;https://github.com/steve-downey/sender-examples&lt;/a&gt;.  The code is also the basis for my talk at C++Now 2023, &lt;a href="https://youtu.be/xXncLUD-4bA?si=tFXFFYLUxia9WW-c"&gt;Using the C++ Sender/Receiver Framework: Implement Control Flow for Async Processing&lt;/a&gt;. They present the code accurately and fairly, and I am very happy they found it useful in describing and understanding the capabilities of Senders in the framework. There is no higher praise than someone finding your work useful to build upon.
&lt;/p&gt;

&lt;p&gt;
Nonetheless, we come to different overall conclusions about the Sender/Receiver framework.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;

&lt;p&gt;
There are several papers in the February 2026 pre-Croydon mailing which are, taken together, asking to reconsider the sender / receiver framework, generally and especially in the context of networking. As this is the meeting in which C++26 will be finalized for publication, this is the last chance for any objections to anything being included. It would definitely have been better to have these objections and any new information earlier, but, since changing anything after the standard is shipped is far more difficult, even sustained opposition with new information is important to consider.
&lt;/p&gt;

&lt;p&gt;
This post, however, is not my consideration of the new papers.
&lt;/p&gt;

&lt;p&gt;
I simply wish to put the work I did in 2022 and 2023 in context.
&lt;/p&gt;
&lt;div id="ox-nikola-outline-container-org9a1dc7a" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org9a1dc7a"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; The Papers&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
The papers which I am looking at
&lt;/p&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;

&lt;col class="org-left"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope="col" class="org-left"&gt;Number&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Title&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Author&lt;/th&gt;
&lt;th scope="col" class="org-left"&gt;Subgroup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;a href="https://wg21.link/P2583R0"&gt;P2583R0&lt;/a&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;Symmetric Transfer and Sender Composition&lt;/td&gt;
&lt;td class="org-left"&gt;Mungo Gill, Vinnie Falco&lt;/td&gt;
&lt;td class="org-left"&gt;LEWG Library Evolution&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;a href="https://wg21.link/P4003R0"&gt;P4003R0&lt;/a&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;Coroutines for I/O&lt;/td&gt;
&lt;td class="org-left"&gt;Vinnie Falco, Mungo Gill, Steve Gerbino&lt;/td&gt;
&lt;td class="org-left"&gt;LEWGI SG18: LEWG Incubator,LEWG Library Evolution&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;a href="https://wg21.link/P4007R0"&gt;P4007R0&lt;/a&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;Senders and Coroutines&lt;/td&gt;
&lt;td class="org-left"&gt;Vinnie Falco, Mungo Gill&lt;/td&gt;
&lt;td class="org-left"&gt;LEWG Library Evolution&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;a href="https://wg21.link/P4014R0"&gt;P4014R0&lt;/a&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;The Sender Sub-Language&lt;/td&gt;
&lt;td class="org-left"&gt;Vinnie Falco, Mungo Gill&lt;/td&gt;
&lt;td class="org-left"&gt;LEWG Library Evolution&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;&lt;a href="https://wg21.link/P4029R0"&gt;P4029R0&lt;/a&gt;&lt;/td&gt;
&lt;td class="org-left"&gt;The SG14 Priority List for C++29/32&lt;/td&gt;
&lt;td class="org-left"&gt;Michael Wong&lt;/td&gt;
&lt;td class="org-left"&gt;SG14 Low Latency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org2881277" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org2881277"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; Abstracts and Suggested Polls&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
These quotes are drawn from their respective papers to provide context.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org61282c0" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org61282c0"&gt;&lt;span class="section-number-3"&gt;2.1.&lt;/span&gt; P2583R0: Symmetric Transfer and Sender Composition&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-1"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orge0e6d56" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orge0e6d56"&gt;&lt;span class="section-number-4"&gt;2.1.1.&lt;/span&gt; Abstract&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-1-1"&gt;
&lt;p&gt;
C++20 provides symmetric transfer (&lt;a href="https://wg21.link/P0913R1"&gt;P0913R1&lt;/a&gt;) - a mechanism where &lt;code&gt;await_suspend&lt;/code&gt; returns a &lt;code&gt;coroutine_handle&amp;lt;&amp;gt;&lt;/code&gt; and the compiler resumes the designated coroutine as a tail call. Coroutine chains execute in constant stack space. &lt;code&gt;std::execution&lt;/code&gt; (&lt;a href="https://wg21.link/P2300R10"&gt;P2300R10&lt;/a&gt;) composes asynchronous operations through sender algorithms. These algorithms create receivers that are structs, not coroutines. No &lt;code&gt;coroutine_handle&amp;lt;&amp;gt;&lt;/code&gt; exists at any intermediate point in a sender pipeline. When a coroutine &lt;code&gt;co_await&lt;/code&gt; s a sender that completes synchronously, the stack grows by one frame per completion. &lt;a href="https://wg21.link/P3552R3"&gt;P3552R3&lt;/a&gt;’s &lt;code&gt;std::execution::task&lt;/code&gt; inherits this property. The sender model’s zero-allocation composition property and symmetric transfer’s constant-stack property cannot both be satisfied. One requires structs. The other requires coroutines. This paper describes the mechanism, provides implementation experience, and documents the tradeoff.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org2fc92a9" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org2fc92a9"&gt;&lt;span class="section-number-3"&gt;2.2.&lt;/span&gt; P4003R0: Coroutines for I/O&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-2"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgbb3a24d" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orgbb3a24d"&gt;&lt;span class="section-number-4"&gt;2.2.1.&lt;/span&gt; Abstract&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-2-1"&gt;
&lt;p&gt;
C++20 coroutines have five properties that, taken together, make them uniquely suited to asynchronous I/O: type erasure through &lt;code&gt;coroutine_handle&amp;lt;&amp;gt;&lt;/code&gt; , customization through promise_type , stackless independently resumable frames, symmetric transfer through await_suspend , and compiler-managed state that persists across suspension points. Each was designed for generality. Their conjunction yields something no single property suggests: the optimal basis for byte-oriented I/O.
&lt;/p&gt;

&lt;p&gt;
We used C++20 coroutines directly for I/O - timers, sockets, DNS, TLS, HTTP - and observed what the language already provides. The protocol that emerged is the IoAwaitable: a system for associating a coroutine with an executor, stop token, and frame allocator, and  propagating this context forward through a coroutine chain to the operating system API boundary where asynchronous operations are performed.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org3d5a105" class="ox-nikola-outline-4"&gt;
&lt;h4 id="org3d5a105"&gt;&lt;span class="section-number-4"&gt;2.2.2.&lt;/span&gt; Suggested Straw Polls&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-2-2"&gt;
&lt;p&gt;
SG4 polled at Kona (November 2023) on &lt;a href="https://wg21.link/P2762R2"&gt;P2762R2&lt;/a&gt; “Sender/Receiver Interface For Networking”
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
“Networking should support only a sender/receiver model for asynchronous operations; the
Networking TS’s executor model should be removed”
&lt;/p&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-right"&gt;

&lt;col class="org-right"&gt;

&lt;col class="org-right"&gt;

&lt;col class="org-right"&gt;

&lt;col class="org-right"&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-right"&gt;SF&lt;/td&gt;
&lt;td class="org-right"&gt;F&lt;/td&gt;
&lt;td class="org-right"&gt;N&lt;/td&gt;
&lt;td class="org-right"&gt;A&lt;/td&gt;
&lt;td class="org-right"&gt;SA&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;5&lt;/td&gt;
&lt;td class="org-right"&gt;5&lt;/td&gt;
&lt;td class="org-right"&gt;1&lt;/td&gt;
&lt;td class="org-right"&gt;0&lt;/td&gt;
&lt;td class="org-right"&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Consensus.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
The approach described in this paper - a coroutine-native I/O model using C++20 language features - was not among the alternatives considered.
&lt;/p&gt;

&lt;p&gt;
Poll 1. A coroutine-native I/O model is a distinct approach from both the Networking TS executor model and the sender/receiver model.
&lt;/p&gt;

&lt;p&gt;
Poll 2. New research into coroutine-native I/O, not available at the time of the Kona poll, warrants consideration.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgd1977f3" class="ox-nikola-outline-3"&gt;
&lt;h3 id="orgd1977f3"&gt;&lt;span class="section-number-3"&gt;2.3.&lt;/span&gt; P4007R0: Senders and Coroutines&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-3"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgea581aa" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orgea581aa"&gt;&lt;span class="section-number-4"&gt;2.3.1.&lt;/span&gt; Abstract&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-3-1"&gt;
&lt;p&gt;
&lt;code&gt;std::execution&lt;/code&gt; serves its domain well. Different asynchronous domains have different costs, and a single model cannot minimize all of them simultaneously. This paper identifies four structural gaps where the sender model meets coroutines: three at the boundary - error reporting, error returns, and frame allocator propagation - and one inside the composition mechanism - the symmetric transfer gap documented in &lt;a href="https://wg21.link/P2583R0"&gt;P2583R0&lt;/a&gt;. Each gap is the cost of a property the sender model requires for compile-time analysis (&lt;a href="https://wg21.link/P2300R10"&gt;P2300R10&lt;/a&gt;, &lt;a href="https://wg21.link/P4014R0"&gt;P4014R0&lt;/a&gt; ). They are not design defects, they are tradeoffs. Mandating that standard networking be built on the sender model would force coroutine I/O users to pay these costs. A coroutine-native I/O research report (&lt;a href="https://wg21.link/P4003R0"&gt;P4003R0&lt;/a&gt;) made the gaps visible by showing that partial results, error returns, cancellation, and frame allocator propagation emerge naturally when I/O is designed for coroutines. The findings hold regardless of that report’s specific design. This paper recommends: ship &lt;code&gt;std::execution&lt;/code&gt; for C++26, defer task (&lt;a href="https://wg21.link/P3552R3"&gt;P3552R3&lt;/a&gt;, “Add a Coroutine Task Type”) to C++29, and explore coroutine-native I/O designs alongside sender-based designs.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orga265672" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orga265672"&gt;&lt;span class="section-number-4"&gt;2.3.2.&lt;/span&gt; Suggested Straw Polls&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-3-2"&gt;
&lt;ol class="org-ol"&gt;
&lt;li&gt;“I/O completions that carry both an error code and a byte count present a design challenge for the three-channel completion model.”&lt;/li&gt;
&lt;li&gt;“The coroutine integration in std::execution has open design questions that would benefit from further iteration.”&lt;/li&gt;
&lt;li&gt;“WG21 should explore coroutine-native I/O designs alongside sender-based designs.”&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org253d9b6" class="ox-nikola-outline-3"&gt;
&lt;h3 id="org253d9b6"&gt;&lt;span class="section-number-3"&gt;2.4.&lt;/span&gt; P4014R0: The Sender Sub-Language&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-4"&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org622817a" class="ox-nikola-outline-4"&gt;
&lt;h4 id="org622817a"&gt;&lt;span class="section-number-4"&gt;2.4.1.&lt;/span&gt; Abstract&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-4-1"&gt;
&lt;p&gt;
C++26 introduces a rich sub-language for asynchronous programming through &lt;code&gt;std::execution&lt;/code&gt; (P2300R10) . Sender pipelines replace C++’s control flow, variable binding, error handling, and iteration with library-level equivalents rooted in continuation-passing style and monadic composition. This paper is a guide to the Sender SubLanguage: its theoretical foundations, the programming model it provides, and the engineering trade-offs it makes. The trade-offs serve specific domains well. The question is whether other domains deserve the same freedom to choose the model that serves them.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgdfaef96" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orgdfaef96"&gt;&lt;span class="section-number-4"&gt;2.4.2.&lt;/span&gt; Suggested Straw Polls&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-4-2"&gt;
&lt;ol class="org-ol"&gt;
&lt;li&gt;“ std::execution serves coroutine-driven async I/O less ideally than heterogeneous compute.”&lt;/li&gt;
&lt;li&gt;“Coroutine-driven async I/O should have the same freedom to optimize for its domain as heterogeneous compute did&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orge1586cf" class="ox-nikola-outline-3"&gt;
&lt;h3 id="orge1586cf"&gt;&lt;span class="section-number-3"&gt;2.5.&lt;/span&gt; P4029R0: The SG14 Priority List for C++29/32&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-2-5"&gt;
&lt;p&gt;
This paper is a forward looking priority list for SG14 Low Latency. It has overlapping concerns about the sender / receiver model with the other papers.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgb7259a3" class="ox-nikola-outline-4"&gt;
&lt;h4 id="orgb7259a3"&gt;&lt;span class="section-number-4"&gt;2.5.1.&lt;/span&gt; Executors/coroutine backward flow + lazy execution vs direct forward flow model for CPU-bound I/O and Networking.&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-2-5-1"&gt;
&lt;ol class="org-ol"&gt;
&lt;li&gt;Decouple Networking from std::execution SG14 advise that Networking (SG4) should not be built on top of &lt;a href="https://wg21.link/P2300"&gt;P2300&lt;/a&gt;. The allocation patterns required by &lt;a href="https://wg21.link/P2300"&gt;P2300&lt;/a&gt; are incompatible with low-latency networking requirements.&lt;/li&gt;
&lt;li&gt;Standardize "Direct Style" I/O Prioritize &lt;a href="https://wg21.link/P4003"&gt;P4003&lt;/a&gt; (or similar Direct Style concepts) as the C++29 Networking model. It offers the performance of Asio/Beast with the ergonomics of Coroutines, maintaining the "Zero-Overhead" principle.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org77f32c0" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org77f32c0"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; The Sender Model&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
As described in my earlier papers and talks, and accurately summarized by &lt;a href="https://wg21.link/P4014R0"&gt;P4014R0: The Sender Sub-Language&lt;/a&gt;, senders are a framework for working with continuations and for supporting &lt;a href="https://en.wikipedia.org/wiki/Continuation-passing_style"&gt;Continuation Passing Style&lt;/a&gt;, or CPS. The CPS formalism is a full model of computation, Turing complete, and equivalent in expressiveness to Single Static Assignment, the current, common, model that compilers use for lowering programming languages. CPS used to be far more common, and is still in use in some language implementations today. The reasons for choosing one over the other is entirely out of scope for this discussion.
&lt;/p&gt;

&lt;p&gt;
The Sender model is also closely tied to the &lt;code&gt;Cont&lt;/code&gt; monad. For background see &lt;a href="https://hackage-content.haskell.org/package/mtl-2.3.2/docs/Control-Monad-Cont.html"&gt;Control.Monad.Cont&lt;/a&gt;, &lt;a href="https://en.wikibooks.org/wiki/Haskell/Continuation_passing_style"&gt;Haskell/Continuation passing style&lt;/a&gt;, and &lt;a href="https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/the-mother-of-all-monads"&gt;The Mother of all Monads&lt;/a&gt;. The monad abstracts away the chaining and connection of the results to the eventual receiver. Senders augment the model by adding the three channels used to communicate between the processes, allowing for propagation of values, errors, and cancellation requests throughout the graph.
&lt;/p&gt;

&lt;p&gt;
The Sender / Receiver framework is also a model of Structured Concurrency, &lt;a href="https://www.250bpm.com/p/structured-concurrency"&gt;Structured Concurrency, Martin Sustrik&lt;/a&gt;, &lt;a href="https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/"&gt;Notes on structured concurrency, or: Go statement considered harmful – Nathaniel J. Smith&lt;/a&gt;, a system for ensuring that concurrent operations have clear entry and exit points and will all complete before exiting. It does so in part by also using delimited continuations, ones that have fixed, rather than unbounded, end points, &lt;a href="https://okmij.org/ftp/continuations/index.html"&gt;Continuations and Delimited Control – Oleg Kiselyov&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
In evaluating a library, particularly one that will be frozen in the C++ standard, where good libraries go to die, I want to know if what is being offered is complete, and if it is not, if what is offered has everything necessary and sufficient to base further work on outside the library. We made this mistake, for example, with the initial Ranges work where there was no mechanism for a user to write a pipe-able algorithm with what was specified. This was quickly rectified, fortunately. With &lt;code&gt;format&lt;/code&gt;, the ability to statically check the format string was back-filled, and was not ABI compatible, causing vendors much distress in shipping the new component.
&lt;/p&gt;

&lt;p&gt;
It was with this in mind that I was looking at what was well specified and available in the original P2300 papers. In general I prefer systems that have firm theory behind them over ones that merely, pragmatically, appear to work. As an industry we were all burned badly by the pthreads library, which appeared to work, but we now know can not by itself. The pthread model is also one that we have demonstrated, repeatedly, that human beings can not reason about. On the other hand, object orientation still has no good theory, but has been demonstrably successful. The abstractions it provides allow humans to reason at a much higher level about the complex systems we are producing.
&lt;/p&gt;

&lt;p&gt;
The work in 2022 and 2023 was with the goal in mind of showing that the end user facing parts of the model, senders and the basic adapters in the proposal, were necessary and sufficient as a basis for further work. That the primitives would not need to be scrapped or changed in ways that would break code once written. This is not the same as saying there need be no other adapters or algorithms provided, or that the sender primitives are the most efficient implementations. The question was how far can the model be pushed without having to work with &lt;code&gt;operational state&lt;/code&gt; directly.
&lt;/p&gt;

&lt;p&gt;
The base sender operations, &lt;code&gt;just&lt;/code&gt;, &lt;code&gt;then&lt;/code&gt;, and &lt;code&gt;let_value&lt;/code&gt;, allow all structured programming constructs to be created, including structured concurrency. The model is complete and well founded.
&lt;/p&gt;

&lt;p&gt;
The proposal, as adopted, also provides for user implementation and inter-operation of types that have operational states, senders and sender adapters themselves. Analogously to how the standard allows for user written containers to participate in the containers/iterators/algorithms triad that comprised the original &lt;a href="https://www.stepanovpapers.com/STL/DOC.PDF"&gt;STL proposal&lt;/a&gt;. Users may not write new containers or iterators often, but there is no particular magic involved, and writing one should be well within the capabilities of most C++ programmers.
&lt;/p&gt;

&lt;p&gt;
A sender is somewhat more difficult, as it is new so there is less documentation, but it is not intrinsically hard. In addition, the APIs tend to encourage correct and safe use. Senders describe the shape of work and should be generally reusable. Programmers will work with senders and sender adaptors several orders of magnitude more than they will write a sender or sender adapter from scratch.
&lt;/p&gt;

&lt;p&gt;
My question was not, what is missing that can be added. That is self evidently lots of things. We are still adding range algorithms and new ranges after years of work and more are in the pipeline for C++29 already. My question was, can real applications be created with nothing more than what is easily available in senders, and can an application programmer who does not wish to create a sender today, still deliver a sender graph of async work.
&lt;/p&gt;

&lt;p&gt;
That was, and is, clearly, to me, yes.
&lt;/p&gt;


&lt;p&gt;
Exported: 2026-06-29 08:50:27
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/the-sender-sub-language/</guid><pubDate>Sat, 28 Feb 2026 23:40:21 GMT</pubDate></item><item><title>Building vcpkg dependencies with project toolchain</title><link>https://sdowney.org/posts/building-vcpkg-dependencies-with-project-toolchain/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="org28c7185"&gt;
&lt;p&gt;
Making sure vcpkg delivers packages built with your toolchain is not hard, but much of the advice on the internet is flat wrong. You need to specify your toolchain both in your project and in the the vcpkg &lt;code&gt;triplet&lt;/code&gt;. There's an airgap between your project and the dependency in &lt;code&gt;vcpkg install&lt;/code&gt;. The CMake settings can't just flow through.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-org64dc077" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org64dc077"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; Toolchain is more than just the compiler&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
Inside a link context everything needs to use the same definitions for everything or your program breaks. If you are lucky it will fail to link. If you are unlucky it will link and explode at runtime. Or you might have the worst kind of undefined behavior–working the way you expect it to. For now.
&lt;/p&gt;

&lt;p&gt;
The &lt;a href="https://eel.is/c++draft/basic#def.odr-16"&gt;&lt;code&gt;One Definition Rule&lt;/code&gt;&lt;/a&gt; tries to say that all of the definitions in a program must mean the same thing. Using different flags for different translation units is a simple way of giving different meanings to definitions. This can range from preprocessor defines in the flags, to conditional compilation based on the compiler, or standard version, to subtle changes in meaning based on the standard version or other flags, such as the literal encoding to render characters and string constants into.
&lt;/p&gt;

&lt;p&gt;
The toolchain should encompass all of the ABI affecting flags, and most flags affect ABI, or at least codegen, in some way. Differing codegen, while not as dangerous as an ABI break, can still be an unpleasant surprise.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org4de4cac" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org4de4cac"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; Using a toolchain with &lt;code&gt;vcpkg&lt;/code&gt; in-project&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
There are several techniques for using vcpkg for dependency management and specifying a toolchain to use for compilation of the artifacts in a project. For an open source project, the problem is generally how to do so without locking everyone into &lt;code&gt;vcpkg&lt;/code&gt;. The current theory for CMake is to provide dependency provider hooks via CMAKE_PROJECT_TOP_LEVEL_INCLUDES, which are a list of files cmake will process before anything else, and in particular before the &lt;code&gt;project&lt;/code&gt; statement. The Beman Project has a primitive dependency provider, &lt;a href="https://github.com/bemanproject/infra/blob/main/cmake/use-fetch-content.cmake"&gt;use-fetch-content.cmake&lt;/a&gt;, that converts &lt;code&gt;find_package&lt;/code&gt; into &lt;code&gt;FetchContent&lt;/code&gt; and downloads and builds from Git URLs. This is an alternative to vendoring dependencies in-tree directly using git submodules or git subtrees.
&lt;/p&gt;

&lt;p&gt;
As an aside, git submodules are like vice-grips–the wrong tool for every job.
&lt;/p&gt;

&lt;p&gt;
For &lt;code&gt;vcpkg&lt;/code&gt;, you can integrate using the same mechanism, setting CMAKE_PROJECT_TOP_LEVEL_INCLUDES to $(VCPKG_ROOT)/scripts/buildsystems/vcpkg.cmake. This is entirely in place of setting the CMAKE_TOOLCHAIN_FILE to vcpkg.cmake, including vcpkg.cmake in a local toolchain file, or using VCPKG_CHAINLOAD_TOOLCHAIN_FILE to instruct vcpkg.cmake to load a toolchain after processing the &lt;code&gt;triple&lt;/code&gt; file.
&lt;/p&gt;

&lt;p&gt;
The initial configure will look something like:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=$(VCPKG_ROOT)/scripts/buildsystems/vcpkg.cmake &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
      -DCMAKE_TOOLCHAIN_FILE=./etc/gcc-16-toolchain.cmake &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
      -B .build -S .
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
With the gcc-16 toolchain file looking something like:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-cmake"&gt;&lt;code&gt;include_guard(GLOBAL)

set(CMAKE_C_COMPILER gcc-16)
set(CMAKE_CXX_COMPILER g++-16)
set(GCOV_EXECUTABLE "gcov-16" CACHE STRING "GCOV executable" FORCE)

set(CMAKE_CXX_STANDARD 26)

set(CMAKE_CXX_FLAGS
    "-Wall -Wextra -std=gnu++26 -Wno-maybe-uninitialized"
    CACHE STRING
    "CXX_FLAGS"
    FORCE)

set(CMAKE_CXX_FLAGS_DEBUG
    "-O0 -fno-inline -g3"
    CACHE STRING
    "C++ DEBUG Flags"
    FORCE
)
set(CMAKE_CXX_FLAGS_RELEASE
    "-Ofast -g0 -DNDEBUG"
    CACHE STRING
    "C++ Release Flags"
    FORCE
)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
    "-O3 -g -DNDEBUG"
    CACHE STRING
    "C++ RelWithDebInfo Flags"
    FORCE
)
set(CMAKE_CXX_FLAGS_TSAN
    "-O3 -g -fsanitize=thread"
    CACHE STRING
    "C++ TSAN Flags"
    FORCE
)
set(CMAKE_CXX_FLAGS_ASAN
    "-O3 -g -fsanitize=address,undefined,leak"
    CACHE STRING
    "C++ ASAN Flags"
    FORCE
)

set(CMAKE_CXX_FLAGS_GCOV
    "-O0 -fno-default-inline -fno-inline -g --coverage -fprofile-abs-path"
    CACHE STRING
    "C++ GCOV Flags"
    FORCE
)

set(CMAKE_LINKER_FLAGS_GCOV "--coverage" CACHE STRING "Linker GCOV Flags" FORCE)

get_filename_component(RPATH "~/.local/lib64" ABSOLUTE)

set(CMAKE_EXE_LINKER_FLAGS
    "-Wl,-rpath,${RPATH}"
    CACHE STRING
    "CMAKE_EXE_LINKER_FLAGS"
    FORCE
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This provides CMake build types &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;RELEASE&lt;/code&gt;, &lt;code&gt;RELWITHDEBINFO&lt;/code&gt;, &lt;code&gt;TSAN&lt;/code&gt;, &lt;code&gt;ASAN&lt;/code&gt;, and &lt;code&gt;GCOV&lt;/code&gt;, overriding the built-in default flags. It exports the coverage processor to use, picked up by my project, and adds rpath to the linked executable to where I have the gcc-16 libstdc++.so.6 installed. GCC recently bumped the &lt;code&gt;GLIBCXX&lt;/code&gt; version number to 35, so it is no longer compatible with the system &lt;code&gt;libstdc++.so.6&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Note that it doesn't have any vcpkg variables or includes.
&lt;/p&gt;

&lt;p&gt;
Building with the &lt;code&gt;cmake&lt;/code&gt; command line flags above will end up calling vcpkg for any packages mentioned in the &lt;code&gt;vcpkg.json&lt;/code&gt; file and providing them to be found by &lt;code&gt;find_package&lt;/code&gt; calls. It's not quite how a cmake dependency provider works, but functions very much the same. However, &lt;code&gt;vcpkg&lt;/code&gt; will build dependencies the way the system &lt;code&gt;triple&lt;/code&gt; defines things, which will be with the system compiler and default flags, and may be entirely incompatible with your project.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orge72892e" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orge72892e"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; Building Dependencies with your Toolchain.&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
The mechanism &lt;code&gt;vcpkg&lt;/code&gt; provides for injecting a toolchain into a dependency is a custom &lt;code&gt;triple&lt;/code&gt; that provides a VCPKG_CHAINLOAD_TOOLCHAIN_FILE. Because of the intermediate invocation of &lt;code&gt;vcpkg&lt;/code&gt;, setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE in your project does not affect how &lt;code&gt;vcpkg&lt;/code&gt; builds or provides anything.
&lt;/p&gt;

&lt;p&gt;
We can, however, smuggle the variable we need through the environment.
&lt;/p&gt;

&lt;p&gt;
The custom &lt;code&gt;triple&lt;/code&gt; I'm using, &lt;code&gt;x64-linux-custom.cmake&lt;/code&gt;,  looks like:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-cmake"&gt;&lt;code&gt;set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)

set(VCPKG_CMAKE_SYSTEM_NAME Linux)

set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "$ENV{PROJECT_VCPKG_TOOLCHAIN}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And I make sure to export PROJECT_VCPKG_TOOLCHAIN into the environment as part of my build. I use a Makefile to drive my project workflow, so it's straightforward to set and export. Other workflow mechanisms are an exercise for the reader.
&lt;/p&gt;

&lt;p&gt;
The cmake invocation picks up flags and environment:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;span class="org-builtin"&gt;export&lt;/span&gt; &lt;span class="org-variable-name"&gt;PROJECT_VCPKG_TOOLCHAIN&lt;/span&gt;=$(realpath ./etc/gcc-16-toolchain.cmake)
cmake -DCMAKE_TOOLCHAIN_FILE=$(PROJECT_VCPKG_TOOLCHAIN) &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
      -DVCPKG_OVERLAY_TRIPLETS=$(realpath ./cmake) &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
      -DVCPKG_TARGET_TRIPLET=x64-linux-custom &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;
      -B .build -S .
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
With this, &lt;code&gt;vcpkg&lt;/code&gt; will use the toolchain I specify to build dependencies. It also treats the VCPKG_CHAINLOAD_TOOLCHAIN_FILE as ABI significant for its build caching. This means that if you include other files into your toolchain, the contents of those files do not count. A small issue if you are using layered toolchain files, sharing some flags between them such as for gcc-15 vs gcc-16 in e.g. a gcc-flags.cmake file. But I also know switching between many compilers is a very minority use case.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orge66601d" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orge66601d"&gt;&lt;span class="section-number-2"&gt;4.&lt;/span&gt; Someone is Wrong on the Internet&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;p&gt;
This took far longer to work out than it should because there is much advice that is flat out wrong.
&lt;/p&gt;

&lt;p&gt;
In particular from AI generated summaries and articles.
&lt;/p&gt;

&lt;p&gt;
Setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE in your project or on the &lt;code&gt;cmake&lt;/code&gt; command line &lt;b&gt;cannot&lt;/b&gt; affect how &lt;code&gt;vcpkg&lt;/code&gt; builds and retrieves dependencies.
&lt;/p&gt;

&lt;p&gt;
You must set a custom &lt;code&gt;triple&lt;/code&gt; file that sets VCPKG_CHAINLOAD_TOOLCHAIN_FILE in order to use that CMake toolchain with &lt;code&gt;vcpkg&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/building-vcpkg-dependencies-with-project-toolchain/</guid><pubDate>Sat, 27 Dec 2025 17:03:15 GMT</pubDate></item><item><title>Substitution is Sometimes a Failure</title><link>https://sdowney.org/posts/substitution-is-sometimes-a-failure/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="orgb7ba95c"&gt;
&lt;p&gt;
Why are optional::transform and optional::and_then not constrained by &lt;code&gt;invocable&lt;/code&gt;?
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-org3ecfbcf" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org3ecfbcf"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; Optional "Monadic" Interface&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
&lt;a href="http://wg21.link/P0798"&gt;Monadic operations for std::optional&lt;/a&gt;
&lt;/p&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;code&gt;transform&lt;/code&gt; is the c++ spelling for &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;fmap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;and_then&lt;/code&gt; is monadic &lt;code&gt;bind&lt;/code&gt; for optional&lt;/li&gt;
&lt;li&gt;&lt;code&gt;or_else&lt;/code&gt; is dual to &lt;code&gt;and_then&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;code&gt;or_else&lt;/code&gt; also has:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Constraints: &lt;code&gt;F&lt;/code&gt; models &lt;code&gt;invocable&lt;/code&gt; and &lt;code&gt;T&lt;/code&gt; models &lt;code&gt;{move,copy}_constructible&lt;/code&gt;.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
&lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;and_then&lt;/code&gt; do not.
&lt;/p&gt;

&lt;p&gt;
They don't work if you don't give them invocables, and rely on &lt;code&gt;invoke_result_t&lt;/code&gt; to compute the result. So why not constrain them?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org2afa4cf" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org2afa4cf"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; SFINAE is shallow&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
The problem is if another template has to be instantiated in order to evaluate the template of interest, and that template has an error, you get an error, not a substitution failure. And, it turns out, lambdas are a common source of the problem and a common case in real use.
&lt;/p&gt;

&lt;p&gt;
Consider the code:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-type"&gt;void&lt;/span&gt; &lt;span class="org-function-name"&gt;f&lt;/span&gt;(&lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;);

&lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-variable-name"&gt;l&lt;/span&gt; = [](&lt;span class="org-keyword"&gt;auto&lt;/span&gt;&amp;amp; &lt;span class="org-variable-name"&gt;y&lt;/span&gt;) {
    f(y);
    &lt;span class="org-keyword"&gt;return&lt;/span&gt; 42;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The two important parts are the &lt;code&gt;auto&amp;amp;&lt;/code&gt; parameter and the implicit deduced return type.
&lt;/p&gt;

&lt;p&gt;
We can rewrite it, to make things possibly more obvious:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;struct&lt;/span&gt; &lt;span class="org-type"&gt;Func&lt;/span&gt; {
    &lt;span class="org-keyword"&gt;template&lt;/span&gt; &amp;lt;&lt;span class="org-keyword"&gt;typename&lt;/span&gt; &lt;span class="org-type"&gt;T&lt;/span&gt;&amp;gt;
    &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-keyword"&gt;operator&lt;/span&gt;&lt;span class="org-function-name"&gt;()&lt;/span&gt;(&lt;span class="org-type"&gt;T&lt;/span&gt;&amp;amp; &lt;span class="org-variable-name"&gt;t&lt;/span&gt;) {
        f(t);
        &lt;span class="org-keyword"&gt;return&lt;/span&gt; 42;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
and to slightly spoil things, the problem code is effectively:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;static_assert(&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;invocable&lt;/span&gt;&amp;lt;Func, &lt;span class="org-type"&gt;int&lt;/span&gt; &lt;span class="org-keyword"&gt;const&lt;/span&gt;&amp;amp;&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
which produces:
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&amp;lt;source&amp;gt;: In instantiation of &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-constant"&gt;Func&lt;/span&gt;::&lt;span class="org-keyword"&gt;operator&lt;/span&gt;()(&lt;span class="org-type"&gt;T&lt;/span&gt;&amp;amp;) [with T = &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;]&lt;span class="org-warning"&gt;'&lt;/span&gt;:
type_traits:2565:26:   required by substitution of &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;_Fn&lt;/span&gt;, &lt;span class="org-keyword"&gt;class&lt;/span&gt; ... &lt;span class="org-type"&gt;_Args&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;static&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;__result_of_success&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;decltype&lt;/span&gt; (declval&amp;lt;&lt;span class="org-type"&gt;_Fn&lt;/span&gt;&amp;gt;()((&lt;span class="org-type"&gt;declval&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;_Args&lt;/span&gt;&amp;gt;)()...)), &lt;span class="org-constant"&gt;std&lt;/span&gt;::__invoke_other&amp;gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-constant"&gt;__result_of_other_impl&lt;/span&gt;::_S_test(&lt;span class="org-type"&gt;int&lt;/span&gt;) [with _Fn = Func; _Args = {&lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;}]&lt;span class="org-warning"&gt;'&lt;/span&gt;
type_traits:2576:55:   required from &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;struct&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;__result_of_impl&lt;/span&gt;&amp;lt;&lt;span class="org-constant"&gt;false&lt;/span&gt;, &lt;span class="org-constant"&gt;false&lt;/span&gt;, Func, &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;&amp;gt;&lt;span class="org-warning"&gt;'&lt;/span&gt;
type_traits:3038:12:   recursively required by substitution of &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;_Result&lt;/span&gt;, &lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;_Ret&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;struct&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;__is_invocable_impl&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;_Result&lt;/span&gt;, &lt;span class="org-type"&gt;_Ret&lt;/span&gt;, &lt;span class="org-constant"&gt;true&lt;/span&gt;, &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;__void_t&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;typename&lt;/span&gt; &lt;span class="org-constant"&gt;_CTp&lt;/span&gt;::&lt;span class="org-type"&gt;type&lt;/span&gt;&amp;gt; &amp;gt; [&lt;span class="org-constant"&gt;with&lt;/span&gt; _Result = &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;__invoke_result&lt;/span&gt;&amp;lt;Func, &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;&amp;gt;; _Ret = &lt;span class="org-type"&gt;void&lt;/span&gt;]&lt;span class="org-warning"&gt;'&lt;/span&gt;
type_traits:3038:12:   required from &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;struct&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_invocable&lt;/span&gt;&amp;lt;Func, &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;&amp;gt;&lt;span class="org-warning"&gt;'&lt;/span&gt;
type_traits:3286:71:   required from &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;bool&lt;/span&gt; &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_invocable_v&lt;/span&gt;&amp;lt;Func, &lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;&amp;gt;&lt;span class="org-warning"&gt;'&lt;/span&gt;
concepts:336:25:   required from &lt;span class="org-type"&gt;here&lt;/span&gt;
&amp;lt;source&amp;gt;:12:10: error: binding reference of type &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;&lt;span class="org-warning"&gt;'&lt;/span&gt; to &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-keyword"&gt;const&lt;/span&gt; &lt;span class="org-type"&gt;int&lt;/span&gt;&lt;span class="org-warning"&gt;'&lt;/span&gt; discards qualifiers
   12 |         f(t);
      |         ~^~~
&amp;lt;source&amp;gt;:2:12: note:   initializing argument 1 of &lt;span class="org-warning"&gt;'&lt;/span&gt;&lt;span class="org-type"&gt;void&lt;/span&gt; f(&lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;)&lt;span class="org-warning"&gt;'&lt;/span&gt;
    2 |     &lt;span class="org-type"&gt;void&lt;/span&gt; f(&lt;span class="org-type"&gt;int&lt;/span&gt;&amp;amp;);
      |            ^~~~
Compiler returned: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;a href="https://compiler-explorer.com/z/W1jT6bxrf"&gt;Compiler Explorer&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
as the compiler is unhappy about trying to call the function &lt;code&gt;f&lt;/code&gt; with a &lt;code&gt;const int&amp;amp;&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
If the lambda or &lt;code&gt;Func&lt;/code&gt; is changed to have a non-deduced return type, the instantiation errors from the check to &lt;code&gt;invocable&lt;/code&gt; go away, although you still get an error calling either with a &lt;code&gt;const int&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
So why do we run into this with &lt;code&gt;transform&lt;/code&gt; if we were to constrain it with &lt;code&gt;invocable&lt;/code&gt; ?
&lt;/p&gt;

&lt;p&gt;
The compiler needs to figure out the overload set in order to resolve which one to use from the set. There are four of them two for the l- and r- value category and two for the const overloads.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;F&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-function-name"&gt;transform&lt;/span&gt;(&lt;span class="org-type"&gt;F&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;) &amp;amp;;
&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;F&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-function-name"&gt;transform&lt;/span&gt;(&lt;span class="org-type"&gt;F&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;) &lt;span class="org-keyword"&gt;const&lt;/span&gt; &amp;amp;;
&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;F&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-function-name"&gt;transform&lt;/span&gt;(&lt;span class="org-type"&gt;F&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;) &amp;amp;&amp;amp;;
&lt;span class="org-keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;F&lt;/span&gt;&amp;gt; &lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-function-name"&gt;transform&lt;/span&gt;(&lt;span class="org-type"&gt;F&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;) &lt;span class="org-keyword"&gt;const&lt;/span&gt; &amp;amp;&amp;amp;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
with differing computations of the resulting &lt;code&gt;optional&lt;/code&gt; being returned.
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;using&lt;/span&gt; &lt;span class="org-type"&gt;U&lt;/span&gt; = &lt;span class="org-type"&gt;invoke_result_t&lt;/span&gt;&amp;lt;F, &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;​::​move(*val))&amp;gt;;
&lt;span class="org-comment-delimiter"&gt;//&lt;/span&gt;&lt;span class="org-comment"&gt;or
&lt;/span&gt;&lt;span class="org-keyword"&gt;using&lt;/span&gt; &lt;span class="org-type"&gt;U&lt;/span&gt; = &lt;span class="org-type"&gt;remove_cv_t&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;invoke_result_t&lt;/span&gt;&amp;lt;F, &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(*val)&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
So, in order to work out what the templated &lt;code&gt;transform&lt;/code&gt;'s signature really is, it has to compute what the invocable returns, and since the invocable has deduced return type, it needs to instantiate it, and instantiating with &lt;code&gt;const int&lt;/code&gt; causes an error.
&lt;/p&gt;

&lt;p&gt;
This is unfortunate.
&lt;/p&gt;

&lt;p&gt;
If we constrain &lt;code&gt;transform&lt;/code&gt; we get the same errors as above. &lt;a href="https://compiler-explorer.com/z/r1qbjx4zG"&gt;See here,&lt;/a&gt; with just enough of an &lt;code&gt;optional&lt;/code&gt; to compile.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgd1788a7" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgd1788a7"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; Constraints, what are they good for&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
Not absolutely nothing.
&lt;/p&gt;

&lt;p&gt;
Constraints in the library:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Constraints: the conditions for the function's participation in overload resolution ([over.match]).
&lt;/p&gt;

&lt;p&gt;
[Note 1: Failure to meet such a condition results in the function's silent non-viability. — end note]
&lt;/p&gt;

&lt;p&gt;
[Example 1: An implementation can express such a condition via a constraint-expression ([temp.constr.decl]). — end example]
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
&lt;a href="https://eel.is/c++draft/description#structure.specifications-3.1"&gt;[structure.specifications] 3.1&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Constraints are for making an overload not exist if the constraint isn't met. It's not a way of signaling an error. Those are &lt;code&gt;Mandates&lt;/code&gt;:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Mandates: the conditions that, if not met, render the program ill-formed.
&lt;/p&gt;

&lt;p&gt;
[Example 2: An implementation can express such a condition via the constant-expression in a static_assert-declaration ([dcl.pre]). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation can express such a condition via a constraint-expression ([temp.constr.decl]) and also define the function as deleted. — end example]
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
&lt;a href="https://eel.is/c++draft/description#structure.specifications-3.2"&gt;[structure.specifications] 3.2&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Asking to run &lt;code&gt;and_then&lt;/code&gt; on a non-invocable probably ought not to say there is no such function, but instead tell you it can't be invoked. I'm now not convinced that &lt;code&gt;or_else&lt;/code&gt; should be constrained this way. It's not significantly better for &lt;code&gt;o.or_else(5)&lt;/code&gt; to fail to resolve, mentioning &lt;code&gt;invocable&lt;/code&gt;, than produce an error that &lt;code&gt;invoke_result_t&lt;/code&gt; doesn't work, or that &lt;code&gt;f&lt;/code&gt; can't be invoked. The kind of error is a minor detail.
&lt;/p&gt;

&lt;p&gt;
Constraints that let you control the choice of alternatives are wonderful, and requires clauses are normal programmer accessible, unlike SFINAE, or even &lt;code&gt;enable_if&lt;/code&gt;. But without an overload set to constrain, there possibly should not be a constraint.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org2cac57a" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org2cac57a"&gt;&lt;span class="section-number-2"&gt;4.&lt;/span&gt; Can we do better?&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;p&gt;
There are some notes in &lt;a href="https://wg21.link/P0798"&gt;P0798&lt;/a&gt; that suggest that Deducing This might help, &lt;a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html"&gt;P0847&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
The idea would to be to NOT have all the value category overloads that need to be checked, but to just have a single one that deduces what &lt;code&gt;this&lt;/code&gt; is and provide it as a template parameter for further use. The contained parameter could be forwarded using &lt;code&gt;forward_like&amp;lt;Self&amp;gt;&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
P0847 has discussion about how deducing this might be applied to optional. There's also discussion of deducing this and the SFINAE-unfriendly auto at &lt;a href="https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/"&gt;C++23’s Deducing this: what it is, why it is, how to use it&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
With the tools we have today, it looks possible, but still slightly messy. I managed to get my implementation of optional to compile and pass its own tests with.
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-c++"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;template&lt;/span&gt; &amp;lt;&lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;F&lt;/span&gt;, &lt;span class="org-keyword"&gt;class&lt;/span&gt; &lt;span class="org-type"&gt;Self&lt;/span&gt;&amp;gt;
    &lt;span class="org-keyword"&gt;requires&lt;/span&gt;(
        &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;invocable&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;F&lt;/span&gt;,
                       &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;::forward_like&amp;lt;&lt;span class="org-type"&gt;Self&lt;/span&gt;&amp;gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;::declval&amp;lt;T&amp;gt;()))&amp;gt;)
&lt;span class="org-keyword"&gt;constexpr&lt;/span&gt; &lt;span class="org-keyword"&gt;auto&lt;/span&gt; &lt;span class="org-variable-name"&gt;transform&lt;/span&gt;(&lt;span class="org-keyword"&gt;this&lt;/span&gt; Self&amp;amp;&amp;amp; self, &lt;span class="org-type"&gt;F&lt;/span&gt;&amp;amp;&amp;amp; &lt;span class="org-variable-name"&gt;f&lt;/span&gt;)
    -&amp;gt; &lt;span class="org-type"&gt;optional&lt;/span&gt;&amp;lt;&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;invoke_result_t&lt;/span&gt;&amp;lt;
        &lt;span class="org-type"&gt;F&lt;/span&gt;,
        &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;::forward_like&amp;lt;&lt;span class="org-type"&gt;Self&lt;/span&gt;&amp;gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;::declval&amp;lt;T&amp;gt;()))&amp;gt;&amp;gt; {
    &lt;span class="org-keyword"&gt;using&lt;/span&gt; &lt;span class="org-type"&gt;U&lt;/span&gt; = &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;invoke_result_t&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;F&lt;/span&gt;,
                                   &lt;span class="org-keyword"&gt;decltype&lt;/span&gt;(&lt;span class="org-constant"&gt;std&lt;/span&gt;::forward_like&amp;lt;&lt;span class="org-type"&gt;Self&lt;/span&gt;&amp;gt;(
                                       &lt;span class="org-constant"&gt;std&lt;/span&gt;::declval&amp;lt;T&amp;gt;()))&amp;gt;;
    static_assert(&lt;span class="org-negation-char"&gt;!&lt;/span&gt;&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_array_v&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;&amp;gt;);
    static_assert(&lt;span class="org-negation-char"&gt;!&lt;/span&gt;&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_same_v&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;, &lt;span class="org-type"&gt;in_place_t&lt;/span&gt;&amp;gt;);
    static_assert(&lt;span class="org-negation-char"&gt;!&lt;/span&gt;&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_same_v&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;, &lt;span class="org-type"&gt;nullopt_t&lt;/span&gt;&amp;gt;);
    static_assert(&lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_object_v&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;&amp;gt; || &lt;span class="org-constant"&gt;std&lt;/span&gt;::&lt;span class="org-type"&gt;is_reference_v&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;&amp;gt;);
    &lt;span class="org-keyword"&gt;if&lt;/span&gt; (self.has_value()) {
        &lt;span class="org-keyword"&gt;return&lt;/span&gt; &lt;span class="org-type"&gt;optional&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;&amp;gt;{&lt;span class="org-constant"&gt;detail&lt;/span&gt;::from_function,
                           &lt;span class="org-constant"&gt;std&lt;/span&gt;::forward&amp;lt;&lt;span class="org-type"&gt;F&lt;/span&gt;&amp;gt;(f),
                           &lt;span class="org-constant"&gt;std&lt;/span&gt;::forward_like&amp;lt;&lt;span class="org-type"&gt;Self&lt;/span&gt;&amp;gt;(self.value_)};
    }
    &lt;span class="org-keyword"&gt;return&lt;/span&gt; &lt;span class="org-type"&gt;optional&lt;/span&gt;&amp;lt;&lt;span class="org-type"&gt;U&lt;/span&gt;&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If there were a &lt;code&gt;std::forward_like_t&lt;/code&gt;, it might be possible to reduce some of the noise in computing the value category used for the &lt;code&gt;T&lt;/code&gt;. I also have not thought extensively about if the requires clause is truly needed in light of the &lt;code&gt;invoke_result_&lt;/code&gt; that can now be used in the trailing return type.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/substitution-is-sometimes-a-failure/</guid><pubDate>Fri, 28 Nov 2025 17:06:58 GMT</pubDate></item><item><title>Nikola Blog Infrastructure</title><link>https://sdowney.org/posts/nikola-blog-infrastructure/</link><dc:creator>Steve Downey</dc:creator><description>&lt;div class="abstract" id="org49ab5ee"&gt;
&lt;p&gt;
I've migrated from WordPress to a static blog generator–Nikola. This is how it works.
&lt;/p&gt;

&lt;/div&gt;

&lt;!-- TEASER_END --&gt;
&lt;div id="ox-nikola-outline-container-org8e98260" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org8e98260"&gt;&lt;span class="section-number-2"&gt;1.&lt;/span&gt; Nikola&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
Nikola is a static site generator written in python. It's mostly boring, which is a very good thing for software meant for use to be. Its home is &lt;a href="https://getnikola.org"&gt;https://getnikola.org&lt;/a&gt; and the source is on github at &lt;a href="https://github.com/getnikola/nikola"&gt;https://github.com/getnikola/nikola&lt;/a&gt;. It's many years old at this point, but not dead.
&lt;/p&gt;

&lt;p&gt;
The interesting part for me is that someone had already written a plugin that compiled org mode files, my preferred markup language, to the html that Nikola wants to publish. It wasn't quite to my tastes, but the changes were all on the org export side in elisp, and the python invocation of emacs to do the translation didn't need particular changes. That update is at &lt;a href="https://github.com/steve-downey/wctm/tree/src/plugins/orgmode"&gt;https://github.com/steve-downey/wctm/tree/src/plugins/orgmode&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
I've also set up staging at a github pages repo rendered at &lt;a href="https://steve-downey.github.io/"&gt;https://steve-downey.github.io/&lt;/a&gt; since it's a static site. The real version is at &lt;a href="https://sdowney.org"&gt;https://sdowney.org&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Conversion was straightforward using the tools that Nikola provided and &lt;a href="https://getnikola.com/handbook.html#importing-your-wordpress-site-into-nikola"&gt;following the directions&lt;/a&gt; for importing a WordPress xml backup.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-orgc5f2f48" class="ox-nikola-outline-2"&gt;
&lt;h2 id="orgc5f2f48"&gt;&lt;span class="section-number-2"&gt;2.&lt;/span&gt; Orgmode plugin&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
Pulling a plugin into my working site was easy
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-zsh"&gt;&lt;code&gt;nikola plugin -i orgmode
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
copied the code from &lt;a href="https://github.com/getnikola/plugins/tree/master/v8/orgmode"&gt;https://github.com/getnikola/plugins/tree/master/v8/orgmode&lt;/a&gt; and I just had to update the main configuration file, &lt;code&gt;conf.py&lt;/code&gt;, to map org files to the orgmode compiler.
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-python"&gt;&lt;code&gt;&lt;span class="org-variable-name"&gt;COMPILERS&lt;/span&gt; &lt;span class="org-operator"&gt;=&lt;/span&gt; {
    &lt;span class="org-string"&gt;"rest"&lt;/span&gt;: [&lt;span class="org-string"&gt;'.txt'&lt;/span&gt;, &lt;span class="org-string"&gt;'.rst'&lt;/span&gt;],
    &lt;span class="org-string"&gt;"markdown"&lt;/span&gt;: [&lt;span class="org-string"&gt;'.md'&lt;/span&gt;, &lt;span class="org-string"&gt;'.mdown'&lt;/span&gt;, &lt;span class="org-string"&gt;'.markdown'&lt;/span&gt;],
    &lt;span class="org-string"&gt;"html"&lt;/span&gt;: [&lt;span class="org-string"&gt;'.html'&lt;/span&gt;, &lt;span class="org-string"&gt;'.htm'&lt;/span&gt;],
    &lt;span class="org-string"&gt;"orgmode"&lt;/span&gt;: [&lt;span class="org-string"&gt;'.org'&lt;/span&gt;],
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
I then updated the source code for the elisp html exporter using code from &lt;a href="https://github.com/steve-downey/wg21org/blob/main/emacs.d/init.el"&gt;https://github.com/steve-downey/wg21org/blob/main/emacs.d/init.el&lt;/a&gt; which installs org from upstream rather than using the built-in orgmode and configures my preferences. I may end up using more of &lt;a href="https://github.com/steve-downey/wg21org/blob/main/ox-wg21html.el"&gt;ox-wg21html.el&lt;/a&gt; as a framework for the exported HTML, only updating to use Bootstrap for css classes instead of my own or the org builtin.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="ox-nikola-outline-container-org70e50df" class="ox-nikola-outline-2"&gt;
&lt;h2 id="org70e50df"&gt;&lt;span class="section-number-2"&gt;3.&lt;/span&gt; LaTeX support via KaTeX&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;p&gt;
I've configure Nikola to support LaTeX via KaTeX rather than MathJax. It is supposed to be a bit faster. This means &lt;b&gt;inline&lt;/b&gt; math like \(x^2\) or \(1 &amp;lt; 2\) or with breaks like:
  \[
  \int_0^\infty e^{-x^2} dx = {{\sqrt{\pi}} \over {2}}
  \]
Or in running text where I want to mention \[ \frac{-b \pm \sqrt{b^2 - 4 a c}}{2a} \] as the quadratic equation.
It does mean I don't have access to latex blocks like
&lt;/p&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-latex"&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;\begin&lt;/span&gt;{&lt;span class="org-function-name"&gt;equation&lt;/span&gt;}
  n_{i+1} = &lt;span class="org-keyword"&gt;\frac&lt;/span&gt;{n_{i} (d-i) (e-1)}{(i+1)}
&lt;span class="org-keyword"&gt;\end&lt;/span&gt;{&lt;span class="org-function-name"&gt;equation&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Maybe I won't miss it.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><guid>https://sdowney.org/posts/nikola-blog-infrastructure/</guid><pubDate>Mon, 23 Dec 2024 18:15:27 GMT</pubDate></item></channel></rss>