Planet Scheme

Friday, October 16, 2026

Retropikzel's blog

Thursday, October 15, 2026

Scheme Requests for Implementation

SRFI 275: URIs, IRIs, and basic paths

SRFI 275 is now in final status.

This SRFI proposes a programming interface for working with RFC 3986 universal resource identifiers (URIs), as well as RFC 3987’s generalization to internationalized resource identifiers (IRIs). This document defines record types, normalization procedures, and conversion between URIs and IRIs. The proposal also specifies a basic programming interface for working with paths in isolation, as this is a pervasive usage of URIs and IRIs. Finally, we contribute a test suite to specify the behaviour of normalization with respect to relative references, which has in the past been a source of divergence between implementations.

by Duncan Guthrie at Thursday, October 15, 2026

SRFI 287: Graphs

SRFI 287 is now in draft status.

This SRFI proposes a programming interface for working with graphs, based on Martin Erwig’s functional graph library design. The FGL design takes a rather different view of graphs from most graph presentations, in that the data structures are persistent, which is a good fit for Scheme. The interface is generic to any node and edge label data-type, as long as a SRFI 128 comparator can be specified.

by Duncan Guthrie at Thursday, October 15, 2026

spritely.institute

End-to-end encrypted relays for OCapN

Creating a direct connection between any two computers (not just from client to server) over the internet is notoriously difficult. Many home networks are behind NATs which prevent new inbound connections. Sometimes these NATs are from the ISP (carrier-grade NATs) which are impossible to work around with port forwarding and/or UPnP. While hole punching techniques can break through NATs, it's far from guaranteed. Thus, we need something that works in the general case: relays.

Relays are servers, accessible over the public internet, that act as a proxy between two computers that cannot directly connect. In Goblins 0.12 we introduced a federated relay named "prelay". While OCapN traffic is encrypted in transit, the prelay crucially lacks end-to-end encryption (E2EE). E2EE secures all the messages so they can only be read by their intended recipient, preventing intemediatory parties (like the relay) from tampering with or reading them. Due to the lack of E2EE, the relay was named "prelay" (pre-relay) to dissuade its use. Thanks to a grant from NLnet, we're happy to announce that the next release of Goblins will include a federated, E2EE relay implementation!

Having a relay that we could recommend has become even more of a critical part of the OCapN story as we've developed Hoot, our Scheme to WebAssembly compiler. The web is a challenging environment in which to build peer-to-peer applications. Most of the patterns and available APIs are designed around client-server architectures and centralized certificate authorities for security. While relays are unsatisfying compared to making direct connections, they enable peer-to-peer applications to communicate in these hostile environments.

In addition to implementing the E2EE relay for Goblins, our NLnet grant also includes writing a specification for the OCapN group so that the relay can be broadly supported by other OCapN implementations.

Design

We wanted OCapN relays to have the following qualities:

  1. Stateless: no storage of user data; easy to replace or migrate
  2. Federated: reduces the power any one relay holds over the wider network
  3. End-to-end encrypted: prevents man-in-the-middle and replay attacks; perfect forward secrecy

Our existing "prelay" already has the first two properties. To get E2EE, we chose to build upon the Noise Protocol.

Noise is actually a well-established framework of different patterns and ciphers. This allows applications to pick the pattern which is most suitable to their needs. It's also a protocol family which has widespread use (WireGuard, WhatsApp, etc.) and has been scrutinised a great deal. None of us at Spritely are cryptographers by training, so using an off-the-shelf protocol also ensures we're avoiding any mistakes in protocol design.

So how can we use Noise with OCapN? When connecting to an OCapN peer, there is a Locator which encodes three components about the remote peer:

  1. Designator: self-authenticating identity (usually a public key)
  2. Transport: a symbol identifying the kind of netlayer it is (such as onion)
  3. Hints: a hashmap of routing data (such as IP, port, etc.)

Each netlayer decides what goes into these fields, and the union of both the designator and transport should uniquely identify the peer. Noise uses two kinds of keys:

  1. static: a key that doesn't change between sessions
  2. ephemeral: a key that is generated fresh for each session

An important step each netlayer needs to consider is something called peer authentication, which verifies that a peer is who they say they are. OCapN requires that there exists at most one connection between any two peers at a given time. Ensuring there is only ever one session between any two peers is essential to allow for object identity comparison and preventing unnecessary 3rd party handoffs. An OCapN implementation will enforce this by reusing connections or closing new inbound connections if one already exists. Without peer authentication, a malicious peer could impersonate another peer causing a denial of service to the peer whose identity was stolen.

As described above, the designator is a public key, which allows the peer to prove their identity by using its private key. Noise has a pattern for this known as XK, where the initiator knows the key of the responder and will verify it. The X is referring to the initiator's static key being transmitted to the responder. The K is referring to the initiator already knowing the responder's public key. The public key of a peer is encoded and placed in the designator for the initiator to use.

Noise also allows the use of different key generation algorithms, cipher suites, and hashes. This is great as we needed to select something which has implementations both in commonly available cryptographic libraries and within the browser (for Hoot). X25519 keys are well supported in all those situations, as are AESGCM cipher suites and SHA256 hashes. These are all available in GnuTLS (what we're using on the Guile VM) and in the SubtleCrypto API (that we're using on the web).

Guile Noise library

Of course, to use Noise we need an implementation. Noise is popular enough that there's quite a few implementations for different languages. Unfortunately, Guile is not one of them, so that meant writing our own Noise library.

The Guile Noise library we've written doesn't require users to know the intricate workings of Noise, but Noise is interesting and so let's take a brief look at how it works and my implementation experience. The Noise specification is actually pretty approachable and is built around three conceptual objects:

  • CipherState: holds a key and a hash; provides encryption, decryption, and rekeying
  • SymmetricState: holds a chaining key and hash output; uses CipherState for encryption, as well as deriving new keys and hashes
  • HandshakeState: holds state for the initial handshake and handles reading and writing of handshake messages

The Noise protocol starts with a handshake. This is turn-based where the initiator goes first and writes their handshake message. The receiver then reads that message and writes their own. This continues until the handshake is complete. After which you only need the CipherState which can encrypt and decrypt messages (and if desired rekey).

Interestingly, Noise defines a small intepreter which defines a limited set of operations. The handshake patterns are then defined in terms of these operations. I won't go through all of them (see the spec if you're interested), but the XK pattern is defined as:

pre-message <- s
-> e es
<- e ee
-> s se

This is a handshake consisting of three messages (and a pre-message). Pre-messages are not exchanged as part of the Noise handshake itself, they're messages which are expected to have happened out-of-band prior to connection. In this case <- s means the static key from the responder is sent to the initiator. This happens by it being encoded in the OCapN peer locator as we discussed above. The arrows show the direction with the left side being the initiator and the right being the responder.

I'll just cover the first message, -> e es. This message is for the initiator to send to the responder as it starts with ->. The operations are:

  • e: generates an emphemeral key, transmits the public part, and mixes it into an internal hash
  • es: derives a new key (from the ephemeral key and responders static key) and mixes it into its own key

This allows a Noise library to easily support lots of different patterns. Each pattern can define it's pre-messages and handshake messages (all of which are defined in the spec) and the implementation just needs to go through and perform the steps.

Once the handshake is complete, the Noise library will create two CipherState objects. The first is used for encrypting and decrypting the initiator's messages, the second for the responder's messages.

The Guile library implements all 12 of the fundamental interactive patterns. We built this library to work on both Hoot and the Guile VM. It currently uses SubtleCrypto for Hoot and GnuTLS for the Guile VM, but it's written so users of the library can add support for other crypto libraries. Our library comes with support for X25519 key generation, AES-GCM ciphers, and SHA256 and SHA512 hash functions. Noise not only provides an approachable spec, but also a set of test vectors to help implementation. These are a set of messages which Noise should exchange for a given pattern and keys. Our library has been tested against all the vectors for patterns and crypto that we support and the test runner checks them on Hoot as well as the Guile VM.

Noise netlayer design

OCapN is a layered protocol consisting of CapTP (Capability Transport Protocol) and many netlayers. A netlayer sends encoded messages between two peers over a particular medium. This allows the higher-level CapTP to be transport agnostic. For example, the Onion netlayer provides transport over Tor Onion services. However, netlayers can be defined not only in terms of a specific transport protocol, but also in terms of another netlayer. We call these meta netlayers.

Noise itself isn't designed with a specific transport protocol in mind, and the Noise netlayer isn't either. Indeed many netlayers might want to rely on the properties of Noise for many different reasons. Even netlayers which provide strong encryption, like Tor, may wish to rely on Noise for its bidirectional peer authentication properties (Tor authenticates outbound connections to a hidden service, but not the reverse).

The Noise netlayer thus composes with any other netlayer. Composing the Noise netlayer and prelay netlayer yields an end-to-end encrypted relay netlayer!

How does this work? The Noise netlayer spawns a sort of "bootstrap" object (not the same bootstrap if you're familiar with CapTP). The bootstrap object is encoded within the OCapN peer locator, and can be enlivened to make a live reference when making a new outbound connection. Each side spawns an inbox and outbox pair, and the initiating peer delivers their outbox to the bootstrap object. The bootstrap object sets up its side of Noise and makes its own inbox-outbox pair returning its outbox. Each side is then able to deliver messages to each other by sending messages to the remote outbox object. Each side then follows the handshake procedure. Once complete, encrypted messages can be exchanged by using the Noise encrypt procedure and decrypted when read from the inbox.

Looking at the test code which uses the TCP+TLS netlayer, layering on Noise encryption is just a few lines of code:

(define a-mycapn
  (with-vat vat-a
    ;; Spawn the TCP+TLS netlayer
    (define tcp-tls-netlayer (spawn ^tcp-tls-netlayer "127.0.0.1"))
    ;; Spawn the Noise netlayer, passing in the TCP+TLS netlayer
    (define noise-netlayer (spawn ^noise-netlayer tcp-tls-netlayer))
    ;; Spawn the MyCapN object (CapTP)
    (spawn-mycapn noise-netlayer)))

The Relay netlayer

So, how does this work with the relay? Well, since we already have an non-E2EE relay and now have seen we can compose Noise and get the properties of both, we can simply pass in the prelay netlayer to Noise netlayer and get an E2EE relay. Of course, we don't want to ask users of the netlayer to do this. We'll be replacing the prelay module with a new relay module that is always end-to-end encrypted.

While the relay design (in its non-E2EE form) isn't new to Goblins, it's worth quickly taking a look at how the relay works. The relay itself is built on the idea of a relay client and a relay server. On the relay server there are two actors:

  1. Controller: allows the client to steer its own relay accepting new inbound connections and making new outbound connections
  2. Endpoint: which is encoded in the OCapN locator and is publicly accessible, a relay server messages this object when establishing a new connection to the peer

This makes the relay federated. Two OCapN peers can use different relay servers and still communicate using OCapN between the two relay servers. When a client wants to make an outbound connection to a peer on another relay server, the relay can enliven the endpoint sturdyref (getting a live object) and send a message to it.

The animation above shows the flow between two peers using federation. It starts with two actors on two different peers, Alice (on peer A) and Bob (on peer B). Alice has a sturdyref (an OCapN locator specifying an object) to Bob. Here's how the flow works:

  1. Peer A first sets itself up with the controller by registering an object a-incoming with the controller. This object can be used when establishing new connections.
  2. Alice has a sturdyref to Bob but wants a a live reference to Bob that she can message, she asks her MyCapN to /enliven/ it.
  3. MyCapN creates an adeliver object which faciliates the connection. It then sends a message to the relay controller with the sturdyref and adeliver object and asks the relay to make a new connection.
  4. The controller extracts the endpoint object for Peer B's relay from the sturdyref and enlivens it forming a new connection between the two relays. It also spawns a new rarelay object to facilitate communication between these two peers and hands this back to adeliver.
  5. The controller then sends the rarelay object to the endpoint on B's relay. B's relay then spawns its own analog to the object rbrelay.
  6. Peer B's relay reaches out to the b-incoming object, allowing it to establish a new connection to Peer B. It passes along the rbrelay object.
  7. Peer B's MyCapN object spawns a new bdeliver object which facilitates its connection.

This gives the two peers a connection to one another, even though neither share a common relay server. The relay is designed to be completely stateless, i.e. it doesn't hold on to data, configuration, or anything else.

One of the lessons we've learned since co-authoring ActivityPub is it's crucial to reduce the impact of servers on their users. In ActivityPub, each instance (a server) stores all the data for its users and performs all the operations their behalf. This means the user needs to trust the server a lot, and if the user's instance shuts down, or they simply wish to migrate, moving to another server is difficult.

Learning from those problems, it was critical for us to make sure the server was stateless. The relay itself stores no state/data for a user and, aside from connecting to another peer, the only behavior it's expected to perform is relaying messages. This makes changing relay servers incredibly easy. The user simply needs to find another (or setup their own) relay server and include that in their sturdy references instead.

Future work

The work isn't done, though. Adding support to Goblins was just one part of the NLnet grant. We'll be contributing specifications for the encrypted relay to the OCapN standardization group so others can implement it. Ridley, who has implemented OCapN in Dart, has already implemented their own Noise library in preparation for the E2EE relay. As part of the grant work within the OCapN group, we'll also be adding support to the test suite to allow both testing new relay implementations, and testing the rest of OCapN over the relay.

Another milestone of the grant is to implement a Guix service to make it easy to run an instance of the relay. The relay doesn't take many resources to run and it has very few dependencies, so it should be easy for others to package for their distributions and run on the cheapest VPS or old computers users might have around their home. We are looking forward to the deployment of a vast network of OCapN relays.

Thanks to our supporters

Your support makes our work possible! If you like what we do, please consider becoming a Spritely supporter today!

Diamond tier

  • Aeva Palecek
  • David Anderson
  • Holmes Wilson
  • Jonathan Frederickson
  • Lassi Kiuru

Gold tier

  • Alex Sassmannshausen
  • Juan Lizarraga Cubillos
  • Larne Pekowsky
  • Serra Allgood

Silver tier

  • Austin Robinson
  • Brit Butler
  • Charlie McMackin
  • Dan Connolly
  • Deb Nicholson
  • Diana Thayer
  • Eric Schultz
  • Evangelo Stavro Prodromou
  • Evgeni Ku
  • Glenn Thompson
  • James Luke
  • Jonathan Smith
  • Jonathan Wright
  • Justin Sheehy
  • Michel Lind
  • Mike Ledoux
  • Moto A
  • Nia Bickford
  • Noah Beasley
  • Steve Sprang
  • Travis Smith

Bronze tier

  • Alan Zimmerman
  • Ben Hamill
  • Benjamin Grimm-Lebsanft
  • Brooke Vibber
  • Brooklyn Zelenka
  • Carl A
  • Crazypedia No
  • Ellie High
  • François Joulaud
  • Gabor Udvari
  • Grant Gould
  • Gregory Buhtz
  • Ivan Sagalaev
  • Jason Wodicka
  • Jeff Forcier
  • Marty McGuire
  • Mason DeVries
  • Michael Orbinpost
  • Neil Brudnak
  • Nelson Pavlosky
  • Philipp Nassua
  • Rodion Goritskov
  • Ron Welch
  • Stefan Magdalinski
  • Stephen Herrick
  • Steven De Herdt
  • Tamara Schmitz
  • Thomas Talbot
  • William Murphy
  • a b
  • chee rabbits
  • r g
  • terra tauri

by Jessica Tallon at Thursday, October 15, 2026

Tuesday, October 13, 2026

The Racket Blog

Racket Infrastructure Compromise

On or before August 10th, a macOS system at Northwestern University which builds and hosts Racket snapshots was compromised. The attackers installed a crypto miner, which was detected by Northwestern network monitoring. By August 18th, the machine was fully deactivated, and was subsequently wiped.

After our investigation, there is no evidence that the attackers did anything other than install a crypto miner, or that they knew the system was connected to the Racket project, or that they used credentials on the system to access any other Racket infrastructure. In particular, we have confirmed that no changes were made to any Racket installers or package catalogs.

However, the system had access to signing keys for signing Apple binaries, and access to a machine that could sign Windows executables with the Racket Programming Language Foundation signing key. Therefore, we have generated new signing keys for all of these purposes. All binaries and installers signed with the potentially compromised keys have been replaced on download.racket-lang.org with newly signed versions. We have informed both Apple and GlobalSign, the issuers of these certificates, of the compromise.

GlobalSign has now revoked the Windows signing certificate. As a result, Windows installers signed with the old certificate may no longer be able to be installed; anyone who downloaded such an installer should download a new one from download.racket-lang.org. However, installed Windows binaries are not affected.

Unfortunately, once the Apple certificate is revoked, existing installations of signed macOS binaries will no longer function. To avoid excessive disruption to students and classes in the middle of the term, we expect to have the Apple certificate revoked at the end of the current semester. However, we recommend that anyone who has a macOS installation of Racket versions from 8.12 through 9.3 install new binaries from download.racket-lang.org now, rather than waiting for the revocation. If you installed “Minimal Racket”, then after reinstalling, you can upgrade all installed packages that include signed libraries with raco pkg update --all.

To ensure that this cannot happen again, we have made several changes to our infrastructure. The relevant machine, now wiped and reinstalled, no longer handles many different Racket services. Additionally, systems that sign macOS snapshot binaries are no longer able to access Windows signing keys. Also, we now use separate signing keys for snapshots and releases, so a problem with snapshot systems cannot affect releases in this way.

We recognize that everyone who uses Racket trusts the project, and trusts us, to provide them with trustworthy software. We apologize for the failure here, and for the difficulties it imposes on all of our users.

Tuesday, October 13, 2026

jointhefreeworld

Where hexagonal architecture shines, and what to reach for instead

Ports and adapters, also called hexagonal architecture, is the pattern I reach for when a system has rules worth protecting, and the pattern I refuse when it has none. This article is the map between those two cases.

If you have read Hexagon of Doom, you know the other half of the argument: on a small service with a thin domain, the hexagon is pure indirection, and the indirection costs more than the boundary buys. On the tech radar, ports and adapters now sits in Adopt, with exactly that qualification in its note: the boundary earns its keep wherever the domain is the complicated part.

Both statements are true at once, and the whole skill is telling which one describes the code in front of you. Read “hexagonal” as a shortening of “hex-a-GO-nal”, and “ports and adapters” as plain English: the domain declares what it needs, and the outside world supplies it.

The one thing the pattern actually requires  #

A port is an interface, owned by the domain, written in the domain’s own words. A adapter is the code outside the domain that satisfies it: a database, an HTTP client, a clock, a message queue, a filesystem. That is the entire pattern. Folders, layers and names like infrastructure/ports/out/ are decoration on top of one rule:

The domain must not depend on the world. The world depends on the domain.

Everything the pattern buys follows from that rule, and nothing else does. The tests you gain are free of I/O because the domain never asked for I/O; the swap you gain is cheap because a swap is one function call in one place; the framework upgrade that does not touch your rules is quiet for the same reason.

The deeper benefit, and the one people forget to mention, is thinking: a port is a sentence about what the domain needs. Writing that sentence out loud is often worth more than the code underneath it, because it forces you to notice that a “payment service” was really a price lookup plus a ledger entry.

Four questions that decide it  #

Before you draw a hexagon, answer these. They are the same questions I use on the radar, and they are honest about the answer “no”.

Question If the answer is yes If the answer is no
Is the domain the complicated part? the boundary protects something worth it you are protecting an empty room
Will there be more than one adapter, for real? the seam pays on the second adapter you built a seam for one implementation
Must the rules run without I/O? the port is the only way to test them the test will hit the database anyway
Will the domain outlive this framework? the boundary is the insurance, and cheap the framework is the system, and you know it

The last one is the question I care about most, and the one that is hardest to answer honestly on a Monday morning. A rule about how invoices are priced will outlive any framework you name. A controller that validates a form will not.

Kotlin and Spring: the pattern as the framework intended  #

Kotlin is a lovely language for ports, because an interface with one method is a sentence, and Spring already does dependency injection well. The port is an interface; the domain service takes one as a constructor parameter; the adapter wears the annotation.

 typealias  Sku =  String
 typealias  Cents =  Int

 data  class  Line( val  sku:  Sku,  val  quantity:  Int)

 //  The port: the domain's own interface, in the domain's own words.
 interface  Catalogue {
     fun  priceOf(sku:  Sku):  Cents
}

 //  The rule, in the only place it is allowed to live.
 class  QuoteService( private  val  catalogue:  Catalogue) {
     fun  total(lines:  List):  Cents =
        lines.sumOf { line -> catalogue.priceOf(line.sku) * line.quantity }
}

The adapter is the only Spring-aware part, and it should be the only part that imports the framework at all:

@ Repository
 class  JdbcCatalogue( private  val  jdbc:  JdbcTemplate) :  Catalogue {
     override  fun  priceOf(sku:  Sku):  Cents =
        jdbc.queryForObject(
             "select price from product where sku = ?",  Int:: class.java, sku,
        ) ?: error( "unknown sku:  $sku ")
}

Two things are worth saying about this. First, Kotlin’s own tools are enough to express the port: an interface, a class, a function type. Where the port has exactly one method, a function type is often better than an interface, because a lambda is the cheapest adapter in the world.

Second, and this is the tell I look for in a review: if an annotation from the framework appears inside the domain, there is no hexagon, whatever the folder names say. A @Transactional on a domain method, a @Entity on an aggregate, a @Component on a service that holds a rule: each one welds the domain to Spring, and the boundary you paid for is gone. Spring’s convenience is not the same thing as an architecture.

Where Spring’s own DI is already enough  #

Spring wires your objects, and that is genuinely most of what a small application needs. If the “domain” is a controller calling two repositories and assembling a response, ports add a second indirection on top of the container, and the container was already the seam. That is the Hexagon of Doom case, in Kotlin, and it is more common than anyone admits.

Guile Scheme: the same shape with none of the ceremony  #

In Scheme you do not need a container to invert a dependency. A port is a record of procedures, the composition root is a function, and the wiring happens in one place that a human being can read in ten seconds. Here is the same shop domain, with the module boundary carrying the architecture:

 ;;;  The domain, and the port it declares.  Nothing here knows that a
 ;;;  database, a web server or a clock exists.
( define-module ( shop domain pricing)
   #:declarative? #t
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
   #:export (make-catalogue catalogue? catalogue-price quote-total))

 ;;;  A catalogue is not a class: it is a record holding one procedure,
 ;;;  from a SKU to a price in cents.  This is the port.
( define-record-type  
  (make-catalogue price-of) catalogue?
  (price-of catalogue-price-of))

( define ( catalogue-price catalogue sku)
  ((catalogue-price-of catalogue) sku))

 ;;;  The rule of the domain, in the only place it is allowed to live.
( define ( quote-total catalogue lines)
   "Return the total in cents of LINES, a list of (SKU . QUANTITY)."
  (fold ( lambda (line total)
          (+ total (* (catalogue-price catalogue (car line)) (cdr line))))
        0
        lines))

An adapter is a function that returns one of those records, and it is impossible to tell a clever adapter from a boring one:

( define-module ( shop adapters catalogue)
   #:declarative? #t
   #:use-module (shop domain pricing)
   #:export (fixed-catalogue alist-catalogue))

 ;;;  For tests and for the REPL: prices known in advance.
( define ( fixed-catalogue prices)
  (make-catalogue ( lambda (sku)
                    ( or (assoc-ref prices sku)
                        (error  "unknown sku" sku)))))

 ;;;  For development: a price list held in memory.
( define ( alist-catalogue rows)
  (make-catalogue ( lambda (sku)
                    ( or (assoc-ref rows sku)
                        (error  "unknown sku" sku)))))

The composition root names the adapter, and it is the only place that does:

( define ( main)
  ( let ((catalogue (alist-catalogue '(( "tea" . 650) ( "pot" . 2400)))))
    (format #t  "total: ~a cents~%"
            (quote-total catalogue '(( "tea" . 2) ( "pot" . 1))))))

Running it shows the arithmetic, and nothing else:

total: 3700 cents

And the test needs no mocking framework, because a catalogue that returns fixed prices is a catalogue:

( define ( test-quote-total)
  ( let ((catalogue (fixed-catalogue '(( "tea" . 100) ( "pot" . 500)))))
    ( unless (= 700 (quote-total catalogue '(( "tea" . 2) ( "pot" . 1))))
      (error  "quote-total is wrong")))
  (display  "quote-total: ok\n"))
quote-total: ok

This is why I keep coming back to Lisp for this pattern. The indirection tax that makes the hexagon expensive in Java-shaped code does not exist here: a port is data, an adapter is a closure, and the REPL lets you replace the adapter while the system runs and ask the domain what it thinks. LucidPlan and Byggsteg both put the data layer behind exactly this kind of seam, and I wrote about the repository eDSL that makes the SQLite adapter one define-repo-method long.

The one discipline Lisp demands is the discipline no compiler will give you: nothing stops you from calling sqlite3 inside the domain module. The protection is your module header, and a reviewer who reads it.

Haskell: the port is a value, and the types keep the promise  #

Haskell gives you the same record-of-functions port, except the compiler now checks the sentence you wrote:

 type  Sku  =  String
 type  Cents  =  Integer

 --  The port: a record of functions.  Nothing about a database here.
 data  Catalogue m  =  Catalogue { priceOf  ::  Sku  -> m  Cents }

 --  The rule, and the only place it is allowed to live.
 quoteTotal  ::  Monad m  =>  Catalogue m  -> [( Sku,  Int)]  -> m  Cents
 quoteTotal catalogue lines  =
  sum  <$> traverse ( \(sku, qty)  -> ( * qty)  <$> priceOf catalogue sku) lines

Now look at the type of quoteTotal. It says the function needs a catalogue and a monad, and it says nothing about a database handle, a connection pool or a logger, because it does not have one. In a language where effects are visible in the type, that is not a comment about your intent: it is a fact about the program.

The usual alternative is a type class ( class Catalogue m where priceOf :: Sku -> m Cents), which reads even better at the call site and is how a lot of production Haskell is written. It has one trap worth naming: a type class with a single instance for the whole program quietly says “everything in this monad may talk to the database”, and blessing all of IO is not the same as handing one component the capability it needs. When I want the boundary to be real, I pass the record.

Domain-driven design is the part that actually pays  #

If you remember one thing from this article, remember this: the abstraction that pays for itself is almost never the architecture, it is the model. Ubiquitous language, aggregates with real invariants, a repository that behaves like a collection of domain objects, domain events where the business already speaks in events. Do that well and the ports fall out almost as a side effect, because a good model has an obvious boundary.

The Repository pattern deserves its own paragraph here, because it is my favourite and the most misused. A repository is not a DAO with a nicer name. It exists so that the domain can say “give me the aggregate with this identity, save this aggregate, forget it” and stay ignorant of SQL, of tables and of the shape of rows. The scheme repository eDSL I linked above does precisely this: one record per repository, one procedure per operation, and the concrete implementation living in a module the domain never imports.

The failure mode is DDD vocabulary without DDD discipline: folders named domain/, entities that are bags of setters, an “aggregate” with no invariant, a “value object” that is a String. That is ceremony, and ceremony is what makes people write articles called Hexagon of Doom. If your model is anemic, no boundary will save it: anemic models are exactly the case where the hexagon has nothing to protect and the indirection is pure cost.

The tiny microservice: here, KISS wins  #

A service that receives a request, validates it, calls two other services and writes one row does not have a domain. It has a flow. Drawing a hexagon around a flow costs you four files per change, a debugging session that crosses three layers, and an onboarding document nobody reads. I have written that code, and I have deleted it again.

The signs that you are in this case, all at once:

  • the rules fit in one function, and that function is under twenty lines;
  • there is exactly one adapter per port, and there will be exactly one for the foreseeable future;
  • the tests are about wiring, not about behaviour;
  • a change to a business rule touches the port, the adapter and the wiring before it touches the rule.

When you see that, collapse the layers. Keep the naming good, keep the validation at the edge where the untrusted data is, and give the domain to the framework’s own mechanisms. The whole point of KISS is not to be lazy; it is to spend your complexity budget on the part of the system that is genuinely hard.

MVC still wins where the application is a conversation about pages  #

Now the part I find myself defending most often, because MVC has become unfashionable while quietly remaining the best fit for a large class of programs: content sites, admin tools, internal CRUD applications, anything where the user’s interaction is “ask for a page, get a page”.

MVC and hexagonal are not rivals. Look at MVC honestly and you find the same dependency arrows: the controller is an adapter for the transport, the view is an adapter for presentation, and the model is the domain. The difference is the cut: MVC slices the system by the request lifecycle, which is exactly the axis along which a page-serving application is understood.

My favourite implementation of that cut is GNU Artanis with SXML and the repository pattern, and this is the stack I reach for by default:

  • The route is the controller. Artanis hands the handler a request context, and the handler asks the repository for the model it needs.
  • The view is data. SXML is a list, so a page is a value. Artanis renders either a template file or an S-expression through the same tpl->html, which means a view can be a function, a fragment or a file, and there is no template language to learn beyond parentheses.
  • The repository is the door to the database. The SQLite implementation stays in its own module, behind the same eDSL as the rest of my projects.

A route in that shape is short enough to read out loud:

 ;;  The route is the controller.  tpl->html takes either a template file
 ;;  or an S-expression, and here the view is a value.
(get  "/jobs/:id"
  ( lambda (rc)
    ( let ((job (job-repo-find rc (params rc  "id"))))
      (tpl->html `(section (@ (class  "job"))
                           (h1 ,(job-ref job))
                           (pre ,(job-command job)))))))

Rendering is the same primitive Guile ships, which is why I can show you the output instead of promising it:

(use-modules (sxml simple))
(sxml->xml `(section (@ (class  "order"))
                     (h2  "order-1")
                     (p  "total: " ,total  " cents")))

order-1

total: 1300 cents

Why this still beats a browser application with a client-side framework for such a site:

  • One source of truth for state. There is no client-side copy of the model to synchronize, so there is no class of bug where the two copies disagree.
  • The browser does what it is good at. Links, forms, back button, bookmarks, printing and a page that works when the JavaScript does not arrive.
  • The whole stack is one language and one process. A stack trace goes from the route to the SQL without crossing a serialization boundary, and the same repository code serves the views and the jobs.
  • HTML is data. SXML means the view can be composed, tested and refactored like any other function, without a template engine that cannot be debugged.

MVC fails when the interaction becomes a conversation about state rather than a request for a page: a drag-and-drop editor, a live dashboard, a collaborative document. There the client is the application, and pretending otherwise is how you end up with a page reload per keystroke. Know which of the two you are building, and pick accordingly: that is the whole decision.

CQRS, when it is interesting  #

Command Query Responsibility Segregation, pronounced in full whenever you have to justify it, is the idea that writing and reading are different jobs, and that the write side should record facts while the read side derives whatever shape the reader wants.

I like to show it in its smallest honest form, with no framework at all:

 ;;;  The write side records facts.  It does not maintain a table, and it
 ;;;  does not know what anybody will read.
( define ( record-order-placed log order total)
   "Return LOG with one more fact: ORDER was placed for TOTAL cents."
  (cons `(order-placed ,order ,total) log))

( define ( record-order-refunded log order amount)
   "Return LOG with one more fact: AMOUNT cents were refunded on ORDER."
  (cons `(order-refunded ,order ,amount) log))

 ;;;  The read side is a fold over the facts.  It holds no state of its
 ;;;  own, so it can be thrown away and rebuilt whenever you ask.
( define ( order-summary log)
  (fold ( lambda (event summary)
          ( cond
           (( and (pair? event) (eq? 'order-placed (car event)))
            (put-summary summary (cadr event) (caddr event)))
           (( and (pair? event) (eq? 'order-refunded (car event)))
            (put-summary summary (cadr event) (- (caddr event))))
           ( else summary)))
        '()
        (reverse log)))

Three facts in, one derived summary out:

facts in the log: 3
read model: ((order-2 . 3700) (order-1 . 500))

That is CQRS in twenty lines: the write side appends facts, the read side folds them. The interesting part is not the code, it is the freedom: a new question about orders is a new fold over the same facts, without touching the write model or migrating anything.

CQRS earns its keep when at least one of these is true:

  • the read side needs several shapes of the same data, and each shape would otherwise be a query with six joins;
  • reads and writes have different load profiles, so they want to scale apart;
  • the history matters as much as the current state, for audit, for replay, or for the business itself;
  • the same facts feed more than one consumer: a screen, a report, a search index, an email digest.

It becomes a bill you cannot pay when there is one read shape, one table and one transaction, because now you own two models and the eventual consistency between them, and the only thing you bought is typing. The tell is a projection that exists to reproduce a SELECT you could have written in an afternoon.

The two gotchas  #

Two failures I have seen more than once, so you can recognize them earlier than I did:

  • Two models, one transaction, no story. CQRS is not free consistency. If your projection must be updated in the same transaction as the write, you have not separated anything; you have renamed it.
  • Events as a data dump. A fact should be written in the language of the business: “order placed”, “refund issued”. An event that carries a whole table row is a change log wearing a domain’s clothes, and it will break the first time you refactor the schema.

Choosing, in one table  #

Situation Reach for Because
Rules are the hard part, several adapters are real ports and adapters the boundary protects the expensive part
A flow: validate, call two services, write a row the framework, plainly there is no domain to protect
Pages asked for by a user, database behind them MVC, a repository, server-rendered views the request lifecycle is the natural cut
Several read shapes over the same facts CQRS, with a projection each question becomes a fold, not a migration
A model you cannot explain to a colleague domain-driven design, before any architecture the model is the abstraction that pays
A framework you might leave in two years a port at that seam, and only there insurance is cheap when it is bought in one place

The rows are not exclusive. My own projects use MVC at the edge, repositories for the data layer, ports where a real second implementation exists, and a fold wherever history is more useful than state. The radar entry and this table say the same thing, and they will keep saying it: match the pattern to the complexity you actually have, and be honest about which part of your system is complicated.

If you take one habit away from all of this, take the repository as a seam and the question “what would I have to change to satisfy this port a second time?”. If the answer is “one file”, you have an architecture. If the answer is “everything”, you have a folder structure.

Tuesday, October 13, 2026

Thursday, October 8, 2026

Scheme Requests for Implementation

SRFI 286: Controlling Library Availability

SRFI 286 is now in draft status.

R7RS Scheme allows programs to query library presence at macro-expansion time using
(cond-expand ((library ⟨library name ⟩) ...)). However, when an implementation ships a shared library tree across interpreters built with different feature configurations, or when third-party libraries depend on conditionally present implementation features, standardizing library presence detection becomes problematic. This proposal specifies library mask files (mask.slm), a declarative mechanism placed in library root directories to dynamically mask unavailable libraries based on the current feature set without requiring physical file removal or upfront parsing of library contents.

by Sergei Egorov at Thursday, October 8, 2026

Wednesday, September 30, 2026

Scheme Requests for Implementation

SRFI 269: Portable Test Definitions

SRFI 269 is now in final status.

This SRFI defines a portable API for test definitions that is decoupled from test execution and reporting. It provides four definition forms: the universal is macro for assertions, testing for giving groups of assertions human-readable context, test for grouping assertions into independently executable units, and suite for organizing tests into hierarchies. The exported metadata auxiliary syntax attaches user-provided information to tests and suites to adjust the behavior of a test runner, for example to select tests by tags or to enforce timeout values. The API is tiny, yet capable and flexible. By focusing on the definition, and leaving execution semantics to test runners, this SRFI offers a common ground that can reduce fragmentation among testing libraries.

Unlike side-effect-driven testing frameworks (e.g. SRFI 64), this API produces first-class runtime entities, making it easy to filter, schedule, wrap them in exception guards and continuation barriers, run in arbitrary order, and re-run dynamically generated test subsets. In addition to the usual CLI test runners, it enables runtime-friendly test runners that integrate well with highly interactive development workflows inside REPLs and IDEs, significantly increasing control over test execution, and shortening the feedback loop.

To bridge the test definitions and test runners, the SRFI specifies a message-passing programming interface and test loading and execution semantics recommendations for test runner implementers.

by Andrew Tropin and Ramin Honary at Wednesday, September 30, 2026

Friday, September 25, 2026

jointhefreeworld

Parsing Expression Grammar in Lisp: Converting Org to HTML with (ice-9 peg) in Guile

Hi everyone. In this blog post I want to take you in an adventure of parsing Org mode with Parsing Expression Grammars (PEG) in Guile Scheme (ice-9 peg) and converting to HTML (via SXML): OrgWebAlchemy.

I wanted to share something with you all that I’ve been working on for a while. It all started with some naive regular expressions to parse Org mode content, but I pretty quickly realized I needed something smarter than that to get to where I want to. It’s taken a while but I am finally more knowledgeable of what Parsing Expression Grammars can do, thanks to GNU’s great (ice-9 peg) module and tutorials.

I thought it might be interesting to people here who enjoy Lisp, Scheme, parsing, Org mode, or the general idea of meta-meta-meta-programming as I like to call it. Disclosure, AI has helped me get a grip of PEG and debug some things, but development of OrgWebAlchemy is “my own spaghetti” and the unit tests and manual verification (and lots of pretty printing the AST) has guided me towards quite a nice implementation (if I may say so myself).

Project’s source code @ Codeberg: https://codeberg.org/jjba23/orgwebalchemy

OrgWebAlchemy is a Guile Scheme library for parsing Org-mode documents into an AST and rendering them to HTML. My main use-case is to export Org to HTML without needing Emacs, and to integrate this feature into some projects of mine, allowing me to write Org mode and have it pretty rendered.

The basic idea is pretty simple:

(use-modules (orgwebalchemy html))

(org->html  "This is ~test~ code.")

becomes something like:

This is < code>testcode
> code.

But the interesting part is what happens in between.

Org document

v Parsing Expression Grammar

v AST

v SXML -> HTML

See here an example showing how OrgWebAlchemy enables the LucidPlan project to render pretty Org mode to HTML



More resources  #

PEG vs. a mountain of regexes?  #

Org-mode looks simple until you actually try to parse it. Headings are easy. A paragraph is easy. A list is easy (wait actually no, this has made me sweat).

And then suddenly you have:

  • nested lists
  • ordered, unordered and description lists
  • different indentation levels
  • inline markup
  • links containing descriptions
  • source blocks
  • example blocks
  • quote blocks
  • tables
  • escaping
  • constructs which must stop consuming input at exactly the right place

At this point, the usual approach of adding another regular expression starts to become somewhat… adventurous. :-)

You end up with things like:

match this, unless that follows it, except inside this block, unless it is a description, but don’t consume the newline, unless the previous line was a list item…

That is not really describing a language anymore. It is describing the history of your parser’s bugs.

So OrgWebAlchemy uses Parsing Expression Grammars (PEGs) through Guile’s excellent (ice-9 peg) module. e.g.

(define-peg-pattern element body
  ( or empty-line
      heading
      separator
      table
      src-block
      quote-block
      example-block
      export-html-block
      description-list
      unordered-list
      ordered-list
      paragraph))

This is rather nice because the grammar itself starts looking like documentation for the language.

And Guile lets us express PEGs directly as S-expressions (alternatively you can also use the more traditional syntax if you don’t like it), which makes the Lisper in me very happy.

One thing I particularly like about this approach is that we have loose coupling and the detail of generating SXML and then rendering HTML is a “presentation concern”. this opens possibilities to later exporting to Markdown or other formats.

For example:

- name :: Josep
- project :: orgwebalchemy
- language :: Scheme

can become an AST along the lines of:

(description-list
 (unordered-item
  (desc-key  "name")
  (line-content  "Josep"))
 ...)

I’m still busy with the exact representation and getting it all right. But as of now v1.0 has some stability :-) I would really love feedback on the project from the great smart people that hang out around here.



Of course Org mode is a huge piece of (great) software, so I am far from supporting all features, but some core important constructs are there:

  • Headings (lines starting by n *)
  • Paragraphs (any “non-special” text)
  • Unordered, Ordered and Description lists (with any level of nesting)
  • Italic, Bold, Inline Code
  • Links with and without description (with nested parsing)
  • Horizontal separators (---–—) five or more dashes
  • Tables
  • #+begin_src
  • #+begin_example
  • #+begin_quote (with nested parsing)
  • #+begin_export html : Org syntax is parsed by your PEG grammar, but raw HTML export blocks bypass the Org inline parser and are emitted as trusted literal output.


YAY recursive lists  #

One of the fun parts has been getting nested Org lists right.

Something like:

- Item 1
  - Item 1.1
  - Item 1.2
- Item 2

should become a quasi-tree

The parser initially produces the flat sequence of list items, and the AST processing phase turns indentation into nested structure.

The HTML renderer can then naturally produce:

< ul>
  < li>
    Item 1
    < ul>
      < li>Item 1.1li
>
      < li>Item 1.2li>
    ul>
  li>
  < li>Item 2li>
ul>

I do still have a small issue here, and that is about the mixing of different list types in nested way. Hopefully it’s a subtle bug to fix.

The HTML side uses SXML, because if we’re already writing Lisp, we might as well represent our HTML as Lisp data too. :-) that really helps a lot and makes building the markup tree so much nicer

I’ve taken care to allow full customization to the output HTML (via Guile parameters) so that the renderer isn’t hard-coded to one particular website’s idea of what HTML ought to look like.Most of them are plain list of classes, but per-heading-level customization is a bit more flexible:

(heading-classes
 ( lambda (level)
   ( case level
     ((1) '( "text-4xl"  "font-bold"))
     ((2) '( "text-2xl"  "font-semibold"))
     ( else '( "text-base")))))

Why am I making this?  #

Partly because I wanted it, I like a challenge, and it’s super fun to work with parsing, ASTs and the lot… I could just use Emacs to do this job as there is no better implementation of Org.

The way it’s coming together though, I like the idea of having a small, hackable, free-software Org parser written in Lisp that other people can extend and customize (perhaps add more renderers, or Org features).

Free software  #

OrgWebAlchemy is licensed under the GNU LGPL v3 or later.

The project is intended to soon be packaged for GNU Guix as guile-orgwebalchemy.

There is also a test suite in the repository which is already proving to be a good safety net and showcase of what the parser can do.



Closing thoughts  #

I’d be really happy to hear your thoughts, especially about the grammar, AST design, parser architecture, or interesting Org constructs that I have not handled yet.

Happy hacking! ✨

Friday, September 25, 2026

Wednesday, September 23, 2026

Retropikzel's blog

Sunday, September 20, 2026

Scheme Requests for Implementation

SRFI 285: Dates and Times

SRFI 285 is now in draft status.

This SRFI details an API for storing and manipluating dates and times. This SRFI decomposes dates and times into dates (a count of days in the proleptic Gregorian calendar), clock times (time of day, independent of time zone), moments (instants in TAI), timezones (a possibly dynamic offset from UTC), timestamps (a date, clock time,and timezone), and time deltas (measurement of elapsed time). By doing this, a Scheme program can operate on days in different calendars and timestamps in different timezones.

by Peter McGoron (steward, corresponding author), John Cowan (original design), Daphne Preston-Kendal (design and specification), and Arvydas Silanskas (implementation) at Sunday, September 20, 2026

Friday, September 18, 2026

Scheme Requests for Implementation

SRFI 284: define-typed

SRFI 284 is now in draft status.

This SRFI defines the forms define-typed and define-typed* for creating procedures with checked type boundaries. It allows checking the arguments and/or the return value or values with simple predicate procedures and raises exceptions on usage when they don’t match. The form define-typed* supports keyword and optional arguments. For ad-hoc procedures for map or fold, it also provides lambda-typed and lambda-typed*. The implementation for Guile is optimized to allow the compiler to use the predicates as guarantees to create more optimized code and provides type information for runtime introspection that supports SRFI 283.

by Arne Babenhauserheide at Friday, September 18, 2026

Thursday, September 17, 2026

crumbles.blog

FAQ: Why isn’t mutable a subtype of immutable, or vice versa?

I remember the moment when I learned about immutability. It changed everything.

— Denis Defreyne

Periodically, in various programming language forums, the discussion comes up of why a certain language doesn’t provide the immutable and mutable variants of some data structure as subtypes or supertypes of one another. Now, it’s not impossible to do this, but it’s actually not formally correct to do so, and by doing so you’ll lose at least some of the type checking guarantees your language can usually make for you.

To understand why this doesn’t work, you have to remember the definition of a subtype. Namely, Liskov’s subsitution principle: a type S is a subtype of T if a value of type S can be used in every context where a value of type T is expected.

As usual when dealing with formal matters, this definition is strictly interpreted. Every really does mean every, not just most. (You might have learned the substitution principle as a mere recommended design pattern for OO classes, but formally speaking a true subtype has to fulfil this criterion.) A static type system which supports subtyping will have to prove this for you in order to pass your program through its type checker.

To illustrate this, let’s take the simplest compound data structure imaginable: the humble pair. Here are our operations on an immutable version.

(cons a d) construct a new pair containing a and d and return it
(car p) return the value of a provided when the pair p was constructed
(cdr p) return the value of d provided when the pair p was constructed

That’s it!

Our mutable variant adds two new operations:

(set-car! p a) change the value of a within the pair p
(set-cdr! p d) change the value of d within the pair p

(And a new constructor, but we’ll deal with that below.)

Now, it should be obvious that an immutable pair can’t be provided where a mutable pair is expected. A place that needs a mutable pair will presumably try to use of these two operations on it, which aren’t defined on an immutable pair, so there will be a typing error.

But why couldn’t it be the other way around? All of the operations provided provided by an immutable pair are also provided by a mutable pair, so it seems like we should be able to use a mutable pair wherever an immutable pair is expected.

The reason is more subtle. The substitution principle extends beyond the set of operations (methods) a type provides to the implicit contract which comes with those operations.

When we take the car or cdr of an immutable pair, we can depend on a contract which says the result will always be the same every time we call it on that pair. This contract means that we can, for example, safely calculate the hash value of the pair based on its contents, store it away in another data structure, and know that it won’t be different when we recalculate it later to try to retrieve it. (In other words, immutability is a prerequisite for hash consing!)

Because of this, immutable and mutable pairs have to be completely different types:

(icons a d) construct a new immutable pair containing a and d and return it
(icar i) return the value of a provided when the immutable pair i was constructed
(icdr i) return the value of d provided when the immutable pair i was constructed

(mcons a d) construct a new mutable pair containing a and d and return it
(mcar m) return the value of a provided when the mutable pair m was constructed
(mcdr m) return the value of d provided when the mutable pair m was constructed
(set-mcar! m a) change the value of a within the mutable pair m
(set-mcdr! m d) change the value of d within the mutable pair m

It’s a typing error if an i is a mutable pair or m is an immutable pair.

Objection: But I’m not mutating it and I really don’t care about the contract of immutability for my use case

Because the two types of pair don’t form a subtype hierarchy, they have to be completely separate types and have separate sets of operations defined on them.

Fortunately, many languages offer one or another mechanism for ad hoc polymorphism, where the same operations can be defined on multiple types even if they don’t form a hierarchy. As a Schemer, I tend to think that this a bad idea in a dynamically typed context, because it makes the reasoning you have to do about the flow of data types vastly more complicated, and thus more difficult to get right. In practice, most languages do offer some mechanism for this, whether dynamically typed or statically typed.

Let’s consider the statically typed case first. Wadler and Blott introduced a mechanism for formally reasoning about ad hoc polymorphism and ensuring the type checker can actually prove it sound. In their terminology, mutable and immutable pairs are different types, but both can belong to a common pair type class whose operations are the original car and cdr we defined above. On mutable pairs, these refer to the underlying mcar and mcdr operations, and on immutable pairs to the icar and icdr operations.

This is still formally sound because the pair type class defines a new contract that says nothing about mutability. In a proper implementation of type classes, the type system will stop you trying to use the mutators in a method where the most you defined about the input type to your function is that they are some kind of pair, mutable or immutable. It won’t prevent you from using the car and cdr operations expecting them to be immutable when they might not be – but it does let you choose the granularity explicitly both ways, declaring the input type to your function as either a mutable pair or immutable pair or either, depending on the contract your function actually expects. A subtype relationship would only allow one way but not the other: you could declare your function as allowing immutable pairs, but potentially incorrectly implicitly including mutable pairs too; or, if it were the other way around, as allowing mutable pairs but potentially incorrectly including immutable ones; but one couldn’t consistently exclude either type (without violating the substitution principle).

Things akin to type classes are available in several statically typed languages, where they’re often called interfaces or traits or roles. However, real world type systems vary greatly in how strictly they enforce the checking.

In dynamically typed, object-oriented languages, this usually takes the form of duck typing where we simply define methods with the same name on multiple different types and let run-time type dispatch do the work. We can still get the benefits by adding explicit check for the presence or absence of the mutation operations before allowing a function to be called. In practice, it’s pretty unusual to do this – especially checking for the absence of mutators – and this is why ad hoc polymorphism in dynamically typed languages tends to invite problems.

Thursday, September 17, 2026

Wednesday, September 16, 2026

Scheme Requests for Implementation

SRFI 283: (Type-)Check Introspection

SRFI 283 is now in draft status.

This SRFI focuses on the introspection and type inference sides of typing / checking. It provides several useful procedures deriving types / checks for arbitrary data. procedure-check-of and check-of work with checks, as in SRFI 253. procedure-type-of and type-of work with implementation-specific types. check->sexp provides a way to serialize opaque checks from above into more readable s-expressions.

by Artyom Bologov at Wednesday, September 16, 2026