<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Adam Nelson</title>
      <link>https://adam.nels.onl</link>
      <description>Personal website and blog of Adam R. Nelson</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://adam.nels.onl/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 03 Sep 2025 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Chatterbox: Federated Chat that Doesn&#x27;t Suck</title>
          <pubDate>Wed, 03 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/chatterbox-federated-chat-that-doesnt-suck/</link>
          <guid>https://adam.nels.onl/blog/chatterbox-federated-chat-that-doesnt-suck/</guid>
          <description xml:base="https://adam.nels.onl/blog/chatterbox-federated-chat-that-doesnt-suck/">&lt;p&gt;Have you used Matrix? Do you like it? Trick question, of course you don’t. It
seems like nobody likes Matrix, at least not once they’re familiar with its…
&lt;em&gt;quirks&lt;&#x2F;em&gt;. It’s frustratingly overengineered, difficult to work with, full of
bugs, and its insistence on E2EE makes it hard for nontechnical users to
comprehend.&lt;&#x2F;p&gt;
&lt;p&gt;For the past several months, I’ve been working on a better solution for
federated chat. Chatterbox is a new protocol that, I hope, will make federated
chatrooms actually nice to use. It’s still in development; currently, I don’t
have any working software to show, just the plan and the API.&lt;&#x2F;p&gt;
&lt;p&gt;But, in case it sounds interesting to you, here’s some of its key features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User-friendly defaults:&lt;&#x2F;strong&gt; The default experience uses ordinary password
login and does not ask you to save a recovery key or anything else complicated.
There will be a default flagship instance at &lt;code&gt;cbox.cx&lt;&#x2F;code&gt; that is only used to host
user accounts that federate to other servers.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client-to-server federation:&lt;&#x2F;strong&gt; When you connect to an instance other than
your home instance, your client connects directly to it, and your home instance
doesn’t know.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optional public key auth:&lt;&#x2F;strong&gt; Power users can choose to identify themselves
with a self-managed Ed25519 keypair. The default password login lets the server
manage your private key, but you can switch to a personal key any time.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Credible exit:&lt;&#x2F;strong&gt; Users can move between instances, switch public keys, and
revoke keys. If you manage your own key, you can move servers even after your
home server goes down or bans you.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No E2EE by default:&lt;&#x2F;strong&gt; Depending on your perspective this may or may not be a
good thing. But in my experience Matrix E2EE is more trouble than it’s worth for
90% of use cases, and there are plenty of uses that don’t need any more
encryption than HTTPS. Future versions may add optional E2EE DMs via MLS.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bot API:&lt;&#x2F;strong&gt; Extra features can be added via bot users, which have access to
additional API features like annotating existing messages and receiving private
&lt;code&gt;&#x2F;&lt;&#x2F;code&gt;-commands. Link previews, for example, are implemented as a bot, but, unlike
Matrix’s maubot, the preview appears an an annotation on the message, not a
separate message.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optional replication:&lt;&#x2F;strong&gt; Servers and rooms exist on one instance by default
(avoiding the Matrix issue of overloading your server by joining a large room),
but can optionally be replicated across multiple instances, for high
availability or as part of a migration process.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simple, easy-to-implement protocol:&lt;&#x2F;strong&gt; The protocol is a single WebSocket
connection with Protobuf messages, using a simple custom RPC system. The entire
API is defined in one &lt;code&gt;.proto&lt;&#x2F;code&gt; file. (I &lt;a href=&quot;..&#x2F;why-not-rest&quot;&gt;considered switching to a REST
API&lt;&#x2F;a&gt; but decided to simplify the WebSocket API instead.)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Interested? I’ll hopefully have more information soon, but for now you can read
the work-in-progress
&lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;ar-nelson&#x2F;75cb64f1f0986ef36d4754fd3115f5c1&quot;&gt;&lt;code&gt;chatterbox.proto&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
that defines the entire protocol. Let me know what you think on Mastodon; I’ll
post updates there with the tag &lt;code&gt;#Chatterbox&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why not REST?</title>
          <pubDate>Sat, 30 Aug 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/why-not-rest/</link>
          <guid>https://adam.nels.onl/blog/why-not-rest/</guid>
          <description xml:base="https://adam.nels.onl/blog/why-not-rest/">&lt;p&gt;When writing a README for Chatterbox (my attempt at a new federated chat
protocol), I wanted to explain all of my design choices. One that quickly gave
me trouble was the API’s format: it uses Protobuf over a single WebSocket
connection, instead of a REST API. Why did I do it this way?&lt;&#x2F;p&gt;
&lt;p&gt;The obvious answer is that it’s just interesting to me. I have a lot of trouble
&lt;a href=&quot;https:&#x2F;&#x2F;mcfunley.com&#x2F;choose-boring-technology&quot;&gt;choosing boring technology&lt;&#x2F;a&gt;, because I have to keep the project
interesting enough for me to stick to it. I reimplemented something like gRPC on
top of WebSockets as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ar-nelson&#x2F;sockrpc&quot;&gt;SockRPC&lt;&#x2F;a&gt;, in order to ensure browser
compatibility and avoid the HTTP&#x2F;2 requirement, and, while it’s not terribly
complicated, it’s still a barrier to understanding the protocol, so I need
a reason why it’s necessary.&lt;&#x2F;p&gt;
&lt;p&gt;But, as I tried to give a better explanation, every reason I could find rang
hollow:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Performance? This is the main one, and WebSockets+Protobuf are almost
certainly faster than REST+JSON, but it’s not like I’ve actually profiled
anything. How much of a difference is it? Is it enough to matter?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Simplicity of sessions and pubkey challenge auth? Chatterbox uses public key
authentication, and one websocket connection with one auth challenge at the
beginning is simpler. But it’s not &lt;em&gt;that&lt;&#x2F;em&gt; hard to have a REST auth endpoint
that generates a JWT after completing a challenge.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Security? Several open chat and social media protocols, including Matrix, have
made the mistake of leaving their uploaded media URLs open to the world, no
auth required. Streaming everything over websocket makes this impossible by
design. But is this much added complexity (streaming media over websocket in
chunks!) really necessary to prevent a simple mistake that could be mentioned
in the spec?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Succinct documentation? In theory, &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;ar-nelson&#x2F;130c2c3563901b59225dd9210d310e38&quot;&gt;the entire Chatterbox protocol can be
defined in one &lt;code&gt;.proto&lt;&#x2F;code&gt; file&lt;&#x2F;a&gt;, using gRPC’s service definition syntax
(since SockRPC reimplements gRPC). But that’s not quite true: there’s SockRPC
itself, as well as out-of-band alternate authentication methods like OAuth.
With a basic REST API, I could use Swagger, which actually has widespread
tooling support.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Binary data? The protocol uses binary public keys and UUIDv7s everywhere, not
to mention timestamps, and by using Protobuf I’ve been able to avoid
committing to a specific string representation of any of these. With JSON (or
REST URLs, even without JSON) I’d have to represent all of these as strings.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;None of these seems like a big enough deal to justify adding more implementation
barriers in the form of a new RPC protocol that will need to be ported to each
new language that implements a Chatterbox client or server.&lt;&#x2F;p&gt;
&lt;p&gt;So what’s standing in the way of me making it a boring old REST API?&lt;&#x2F;p&gt;
&lt;p&gt;Event streaming, mostly. Chatterbox, as currently defined, has 3 types of
streams: user event streams (one per client), server event streams (one per
server the user has joined), and room event streams (one per room, but only one
should be open at a time). These will have to be implemented with either
WebSockets or Server-Sent Events.&lt;&#x2F;p&gt;
&lt;p&gt;SSE is the simpler approach: just give each type of event its own URL. But,
surprisingly, &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Server-sent_events&#x2F;Using_server-sent_events#listening_for_custom_events&quot;&gt;SSE has a limit on the number of open connections&lt;&#x2F;a&gt;, so this
won’t work reliably unless I enforce HTTP&#x2F;2 (at which point why not just use
gRPC?).&lt;&#x2F;p&gt;
&lt;p&gt;So we’re back to WebSockets, now as part of the API instead of the whole thing.
And this still makes it inelegant: WebSockets can’t be described by Swagger, for
example. Certainly it would be much simpler to describe and implement
a WebSocket connection that does simple pub&#x2F;sub on event streams using JSON
instead of a full RPC protocol with error handling and 3 types of streaming
calls.&lt;&#x2F;p&gt;
&lt;p&gt;There are a bunch of places to land in this design space and I’m unsure what’s
worth prioritizing. Do I use REST with WebSockets just for events, but use
Protobuf or CBOR instead of JSON (and accept that that prevents me from
desribing the API with Swagger)? Maybe I should do REST+JSON for everything but
the WebSockets, which can use a binary format? Should I stop trying to force
something clever and just make it all JSON?&lt;&#x2F;p&gt;
&lt;p&gt;If you have thoughts or feedback, you can reply &lt;a href=&quot;https:&#x2F;&#x2F;toot.nels.onl&#x2F;@adam&#x2F;115119481028785546&quot;&gt;on Mastodon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Maybe Everything Is a Coroutine</title>
          <pubDate>Mon, 12 Feb 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/maybe-everything-is-a-coroutine/</link>
          <guid>https://adam.nels.onl/blog/maybe-everything-is-a-coroutine/</guid>
          <description xml:base="https://adam.nels.onl/blog/maybe-everything-is-a-coroutine/">&lt;p&gt;I was inspired, after reading the excellent blog post &lt;a href=&quot;https:&#x2F;&#x2F;without.boats&#x2F;blog&#x2F;let-futures-be-futures&#x2F;&quot;&gt;Let Futures Be
Futures&lt;&#x2F;a&gt;, by the author’s
thought experiment of a language in which all functions are coroutines and this
is used to express asynchronicity: async functions can yield a type called
&lt;code&gt;Pending&lt;&#x2F;code&gt; when awaiting some async action, while pure, synchronous functions can
yield &lt;code&gt;Never&lt;&#x2F;code&gt;, indicating that they never yield at all.&lt;&#x2F;p&gt;
&lt;p&gt;The more I thought about this, the more I realized that a strongly-typed
language in which &lt;em&gt;every function is a coroutine&lt;&#x2F;em&gt; could actually have a lot of
nice properties. I’ve come up with a design with several cool features,
including:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A type system in which coroutines are basically state machines&lt;&#x2F;li&gt;
&lt;li&gt;A typed (algebraic) effect system based on coroutines
&lt;ul&gt;
&lt;li&gt;Functions can be generic over effects and purity&lt;&#x2F;li&gt;
&lt;li&gt;Effects can be “intercepted” by a caller and faked&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;A powerful exception system based on simple sum types
&lt;ul&gt;
&lt;li&gt;An exception pass-through system that feels like a saner, generic version
of checked exceptions&lt;&#x2F;li&gt;
&lt;li&gt;Common Lisp-style resumable conditions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Immutable data structures and pure-by-default functions, but with the ability
to use mutation in algorithms, by keeping all mutation in a function’s scope
and using coroutines to control state&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;How does it work? There are three key features: &lt;em&gt;sequence types&lt;&#x2F;em&gt;, the &lt;em&gt;coroutine
for loop&lt;&#x2F;em&gt;, and the &lt;em&gt;pass operator&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sequence-types&quot;&gt;Sequence Types&lt;&#x2F;h2&gt;
&lt;p&gt;Ordinary functions have &lt;em&gt;argument types&lt;&#x2F;em&gt; and a &lt;em&gt;return type&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fib(n: Int) -&amp;gt; Int
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this everything-is-a-coroutine language, instead of a return type, every
function has a &lt;em&gt;sequence type&lt;&#x2F;em&gt;. This is a sum type describing every type that
the function can yield and every type that it can be resumed with after a yield.&lt;&#x2F;p&gt;
&lt;p&gt;For example, a coroutine version of &lt;code&gt;fib&lt;&#x2F;code&gt; that, instead of returning the &lt;code&gt;n&lt;&#x2F;code&gt;th
Fibonacci number, yields an infinite sequence of the Fibonacci numbers:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fib() -&amp;gt; Int()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;()&lt;&#x2F;code&gt; means that, after the coroutine yields an &lt;code&gt;Int&lt;&#x2F;code&gt;, it can be &lt;em&gt;resumed&lt;&#x2F;em&gt;
with no arguments. Because there is no base case (no branch that cannot be
resumed), this coroutine must be infinite; it continues until the caller chooses
not to resume it.&lt;&#x2F;p&gt;
&lt;p&gt;Consider instead a coroutine that yields a &lt;em&gt;finite&lt;&#x2F;em&gt; sequence of &lt;code&gt;Int&lt;&#x2F;code&gt;s:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;factors(n: Int) -&amp;gt; Int() | Void
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This sequence type has two &lt;em&gt;cases&lt;&#x2F;em&gt;: either it emits an &lt;code&gt;Int&lt;&#x2F;code&gt; and can be resumed
with no arguments, or it emits nothing and cannot be resumed.&lt;&#x2F;p&gt;
&lt;p&gt;Notice the syntax of &lt;code&gt;Void&lt;&#x2F;code&gt;: a type with no parentheses is a valid sequence
type. This means that the ordinary function &lt;code&gt;fib(n: Int) -&amp;gt; Int&lt;&#x2F;code&gt; is a valid
coroutine as well—it yields &lt;code&gt;Int&lt;&#x2F;code&gt; once, then cannot be resumed. Pure functions
are a subset of coroutines!&lt;&#x2F;p&gt;
&lt;p&gt;These strongly-typed coroutines are essentially state machines. Let’s define an
additional piece of syntax: a &lt;em&gt;symbol&lt;&#x2F;em&gt; &lt;code&gt;:foo&lt;&#x2F;code&gt; is a unique singleton object whose
type is also &lt;code&gt;:foo&lt;&#x2F;code&gt;. Using symbols, we can define a simple state machine for
a door:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;door() -&amp;gt;
  | :open(:close)
  | :closed(:open | :lock)
  | :locked(:unlock)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each state accepts only a specific set of actions. An open door can only be
closed; a locked door can only be unlocked; a closed door can be opened or
locked.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coroutine-for-loop&quot;&gt;Coroutine For Loop&lt;&#x2F;h2&gt;
&lt;p&gt;But how do we &lt;em&gt;use&lt;&#x2F;em&gt; these coroutines? The syntax for basic function calls
remains the same; if a coroutine has only no-resume types in its sequence type,
then it can be used directly as an argument or assigned to a variable.&lt;&#x2F;p&gt;
&lt;p&gt;For more complex coroutines, there is a for loop syntax. Consider the door state
machine from before; this is an example of a for loop that uses it:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;for (door()) {
  | :locked -&amp;gt;
    continue(:unlock)
  | :closed -&amp;gt;
    continue(:open)
  | :open -&amp;gt;
    break
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This loop pattern-matches on the values yielded by &lt;code&gt;door&lt;&#x2F;code&gt;. The branches use the
familiar keywords &lt;code&gt;continue&lt;&#x2F;code&gt; and &lt;code&gt;break&lt;&#x2F;code&gt;, but &lt;code&gt;continue&lt;&#x2F;code&gt; takes arguments now: it
resumes &lt;code&gt;for&lt;&#x2F;code&gt;’s coroutine with new arguments, which must match the argument type
of the current branch of the sequence type. The loop does not end until it
encounters a &lt;code&gt;break&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can add some conveniences for the common case of coroutines that yield a list
of values without any resume arguments. &lt;code&gt;continue()&lt;&#x2F;code&gt; is inferred at the end of
blocks whose resume type is &lt;code&gt;()&lt;&#x2F;code&gt;,  &lt;code&gt;break&lt;&#x2F;code&gt; is inferred at the end of blocks with
no resume type, &lt;code&gt;Void&lt;&#x2F;code&gt; branches can be omitted, and wildcard matches are assumed
to not match &lt;code&gt;Void&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;def someNumbers() -&amp;gt; Int() | Void {
  yield 1
  yield 2
  yield 3
}

var total
for (someNumbers()) { n -&amp;gt;
  total += n
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;the-pass-operator&quot;&gt;The Pass Operator&lt;&#x2F;h2&gt;
&lt;p&gt;Extending this coroutine syntax to support algebraic effects and error handling
is straightforward. Errors can be yielded as ordinary values, which usually do
not resume:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;divide(divisor: Int, dividend: Int) -&amp;gt; DivideByZeroError | Int
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Algebraic effects can be yielded, presumably all the way up to the main
function, which will then resume with a response to the given effect. These get
complex, but I’ll introduce syntax sugar to deal with these huge signatures
later.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;readFile(filename: String) -&amp;gt;
  | IOFileStat(FileError | FileInfo)
  | IOFileRead(FileError | Bytes)
  | FileError
  | Bytes
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But actually &lt;em&gt;using&lt;&#x2F;em&gt; effects and errors with the mechanisms we already have (for
loops and &lt;code&gt;yield&lt;&#x2F;code&gt;) would be extremely cumbersome. It would look a lot like Go:
manually checking for error states or effects everywhere, just to &lt;code&gt;yield&lt;&#x2F;code&gt; them
to the next level in the stack.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;readConfigJson(filename: String) -&amp;gt;
  | IOFileStat(FileError | FileInfo)
  | IOFileRead(FileError | Bytes)
  | FileError
  | JsonParseError
  | JsonFormatError
  | ConfigFile
{
  for (readFile(filename)) {
    | IOFileStat a -&amp;gt; continue(yield a)
    | IOFileRead a -&amp;gt; continue(yield a)
    | FileError e -&amp;gt; yield e
    | Bytes data -&amp;gt;
      &amp;#x2F;&amp;#x2F; borrowing some Scala syntax
      parseJson(data) match {
        | JsonParseError e -&amp;gt; yield e
        | JsonValue json -&amp;gt; yield parseConfigFile(json)
      }
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The common pattern here is &lt;code&gt;a -&amp;gt; continue(yield a)&lt;&#x2F;code&gt; (for branches that resume)
and &lt;code&gt;e -&amp;gt; yield e&lt;&#x2F;code&gt; (for branches that don’t). This pattern is so common in this
style of code that it deserves its own piece of syntax sugar: the &lt;em&gt;pass
operator&lt;&#x2F;em&gt;, &lt;code&gt;^&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Placing &lt;code&gt;^&lt;&#x2F;code&gt; before an expression causes any value yielded by that expression
that can be directly yielded (as &lt;code&gt;continue (yield a)&lt;&#x2F;code&gt; or just &lt;code&gt;yield a&lt;&#x2F;code&gt;) to be
yielded. In other words, it causes any types contained in the function’s
sequence type to be &lt;em&gt;passed through&lt;&#x2F;em&gt; from this expression to the caller.&lt;&#x2F;p&gt;
&lt;p&gt;This greatly simplifies the example function:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;readConfigJson(filename: String) -&amp;gt;
  | IOFileStat(FileError | FileInfo)
  | IOFileRead(FileError | Bytes)
  | FileError
  | JsonParseError
  | JsonFormatError
  | ConfigFile
{
  val data = ^readFile(filename)
  val json = ^parseJson(data)
  yield parseConfigFile(json)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The sequence type is still a mess, though. And there’s another problem: what if
a function yields a bunch of effect types, but also a type that you want to
capture in a variable… that also happens to be in your function’s sequence
type?&lt;&#x2F;p&gt;
&lt;p&gt;Here are some changes that solve these problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The keyword &lt;code&gt;infer Type&lt;&#x2F;code&gt; can be used in any branch of the sequence type; its
type is inferred as all subtypes of &lt;code&gt;Type&lt;&#x2F;code&gt; that are yielded by the function.
Values passed through by the &lt;code&gt;^&lt;&#x2F;code&gt; operator are included in this inference.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;infer Type(*)&lt;&#x2F;code&gt; also infers the resume arguments; this can infer multiple
branches.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;^&lt;&#x2F;code&gt; operator can occur in front of sequence type branches. This causes &lt;code&gt;^&lt;&#x2F;code&gt;
in the function body to only pass through types that also have a &lt;code&gt;^&lt;&#x2F;code&gt; in front
of them in the sequence type.&lt;&#x2F;li&gt;
&lt;li&gt;A block or keyword, like &lt;code&gt;nopass&lt;&#x2F;code&gt;, could prevent a type from being passed
through; this would work like a catch block, ensuring the function can handle
a specific yielded type.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With these changes, and assuming that supertypes &lt;code&gt;FileIO&lt;&#x2F;code&gt; and &lt;code&gt;Error&lt;&#x2F;code&gt; exist, the function becomes even simpler!&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;readConfigJson(filename: String) -&amp;gt; ^infer IOFile(*) | ^infer Error | ConfigFile {
  val data = ^readFile(filename)
  val json = ^parseJson(data)
  yield parseConfigFile(json)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it All Together&lt;&#x2F;h2&gt;
&lt;p&gt;These three key features are a surprisingly solid core for a language, but they
aren’t a full language on their own. They could fit into several different
language designs, but, in my opinion, the ideal language built on this
foundation would have:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Strict functional purity outside of algebraic effects and mutable local
variables. You can only perform side effects by yielding effect objects from
&lt;code&gt;main&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;Mutable local variables can still allow for efficient mutable data
structures, thanks to coroutines. A mutable, resizeable array type can
exist, but it cannot be yielded, passed as an argument, or stored in
a struct. Structures of these arrays can then be manipulated by passing
a coroutine into a function, then using actions yielded by that coroutine
to manipulate the arrays defined in that function’s scope.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Immutable, nominally-typed structs with subtyping.&lt;&#x2F;li&gt;
&lt;li&gt;A generic tuple type, allowing for tuples-with-symbols message passing a la
Erlang, but strongly typed.&lt;&#x2F;li&gt;
&lt;li&gt;Full higher-order generics. This allows functions to be generic over purity
and generic over checked exceptions, two qualities that are very rarely found
in type systems.&lt;&#x2F;li&gt;
&lt;li&gt;Type aliases for sequence types, which can include multiple branches. The
syntax for this would likely use &lt;code&gt;(*)&lt;&#x2F;code&gt;. For example, you can define &lt;code&gt;type Ints -&amp;gt; Int() | Void&lt;&#x2F;code&gt;—the &lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt; indicates that it’s a sequence type—and then use
it like this: &lt;code&gt;someFunction() -&amp;gt; Ints(*)&lt;&#x2F;code&gt;.
&lt;ul&gt;
&lt;li&gt;Using these types, functions can take sequence arguments. For example,
&lt;code&gt;sum(seq -&amp;gt; Ints(*)) -&amp;gt; Int&lt;&#x2F;code&gt; is a sum function over a finite sequence of
&lt;code&gt;Int&lt;&#x2F;code&gt;. It can be called with &lt;code&gt;sum(someFunction())&lt;&#x2F;code&gt;, and the &lt;em&gt;entire
sequence&lt;&#x2F;em&gt; returned by &lt;code&gt;someFunction&lt;&#x2F;code&gt; will be passed in to &lt;code&gt;sum&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;A syntax for standalone sequences should also exist, basically the
coroutine equivalent of an
&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;IIFE&quot;&gt;IIFE&lt;&#x2F;a&gt;. For
example, &lt;code&gt;-&amp;gt; { yield 1 }&lt;&#x2F;code&gt; would be a sequence of type &lt;code&gt;-&amp;gt; Int&lt;&#x2F;code&gt; by default,
but could also be explicitly typed: &lt;code&gt;-&amp;gt; Int() | Void { yield 1 }&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Sequence literals could be written with square brackets: &lt;code&gt;[1, 2, 3]&lt;&#x2F;code&gt; would
be syntax sugar for &lt;code&gt;-&amp;gt; Int() | Void { yield 1; yield 2; yield 3 }&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I have no idea when I’ll find the time to actually work on this, but I’d like to
see this language come together. I feel like this everything-is-a-coroutine type
system has a lot of potential as a new architecture for functional programming.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Parentheses are Just Typechecking</title>
          <pubDate>Tue, 22 Jun 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/parentheses-are-just-typechecking/</link>
          <guid>https://adam.nels.onl/blog/parentheses-are-just-typechecking/</guid>
          <description xml:base="https://adam.nels.onl/blog/parentheses-are-just-typechecking/">&lt;p&gt;Consider this Scheme expression, taken from &lt;a href=&quot;http:&#x2F;&#x2F;www.rosettacode.org&#x2F;wiki&#x2F;Mandelbrot_set#Scheme&quot;&gt;the Rosetta Code example for the
Mandelbrot set&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>An Object-Oriented Language for the &#x27;20s</title>
          <pubDate>Fri, 12 Mar 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/an-oo-languge-for-the-20s/</link>
          <guid>https://adam.nels.onl/blog/an-oo-languge-for-the-20s/</guid>
          <description xml:base="https://adam.nels.onl/blog/an-oo-languge-for-the-20s/">&lt;p&gt;Object-oriented programming is out of fashion now, and it has been for a while.
Rarely are new programming languages &lt;em&gt;intentionally&lt;&#x2F;em&gt; object-oriented. And there
are good reasons for this: OO often requires a lot of boilerplate, it forces
code into unnatural object hierarchies, and it encourages hidden mutable state.&lt;&#x2F;p&gt;
&lt;p&gt;But, if we made a new statically-typed OO language from scratch in 2021,
something in the vein of Java or C#, taking everything we’ve learned from
functional programming and a decade-plus of scathing OO criticism, could we fix
this? Especially if we had no expectation of compatibility with legacy code?&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Converting CJK Fonts to WOFF is Harder Than You&#x27;d Think</title>
          <pubDate>Sat, 09 May 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://adam.nels.onl/blog/converting-cjk-fonts-to-woff/</link>
          <guid>https://adam.nels.onl/blog/converting-cjk-fonts-to-woff/</guid>
          <description xml:base="https://adam.nels.onl/blog/converting-cjk-fonts-to-woff/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;tl;dr:&lt;&#x2F;strong&gt; Don’t even try online converters. They’ll drop the CJK characters.
Just install &lt;code&gt;woff-tools&lt;&#x2F;code&gt; and&#x2F;or &lt;code&gt;woff2&lt;&#x2F;code&gt; on your own machine. Most Linux
package managers will have them; on Windows, you can use the EXEs in
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;adrianba&#x2F;woff-tools&quot;&gt;this GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A few days ago, I needed to install a web font (&lt;a href=&quot;https:&#x2F;&#x2F;fonts.google.com&#x2F;specimen&#x2F;Noto+Sans+JP&quot;&gt;Noto Sans JP&lt;&#x2F;a&gt;) for
Japanese text on a site I was working on. The site supports IE11, so I would at
least need a WOFF file, and a WOFF2 for newer browsers.&lt;&#x2F;p&gt;
&lt;p&gt;The obvious place to look was Google Fonts. It used to provide a ZIP download of
any web font in multiple formats (at least TTF, WOFF, and WOFF2). But Google
Fonts seems to have removed this feature; ZIP downloads only include OTF files
now. If I wanted IE11 support, I’d need to convert these to WOFF.&lt;&#x2F;p&gt;</description>
      </item>
    </channel>
</rss>
