Planet Scheme

Thursday, October 30, 2025

Retropikzel's blog

Tuesday, October 28, 2025

Joe Marshall

A Method for Implementing First-Class Continuations on the JVM and CLR (AI assisted)

For this complex topic I needed some help. I explained the process to an AI and had it help me write this blog post. Questions and comments are welcome.

Managed runtimes like the Java Virtual Machine (JVM) and the Common Language Runtime (CLR) provide robust, high-performance environments for software execution. A key feature of these platforms is a rigidly structured call stack, which manages function calls and returns in a strict last-in, first-out (LIFO) order. While this model is efficient and simplifies memory management, it precludes certain powerful control flow constructs, most notably first-class continuations.

A first-class continuation is the reification of the current point of execution—essentially, "the rest of the program"—as an object that can be stored, passed around, and invoked. Invoking a continuation effectively discards the current execution stack and replaces it with the captured one. This document details a methodology for implementing such a mechanism within an interpreter running on a managed runtime, circumventing the limitations of the native call stack.

This document provides a comprehensive technical overview of a method for implementing first-class continuations within an interpreter executing on a managed runtime, such as the JVM or CLR. These platforms enforce a strict, stack-based execution model that is incompatible with the control-flow manipulations required for first-class continuations. The technique described herein circumvents this limitation by creating a custom, manually-managed execution model based on a trampoline and a universal "step" contract, enabling the capture, storage, and invocation of the program's execution state.

1. The Core Execution Architecture

The foundation of this system is an interpreter where every evaluatable entity—from primitive operations to user-defined functions—adheres to a single, uniform execution contract. This approach abstracts execution away from the host's native call stack.

1.1. The `Step` Method

All computable objects implement a `Step` method. This method performs one atomic unit of computation. Its precise signature is critical to the entire mechanism:

bool Step(out object ans, ref IControl ctl, ref IEnvironment env)

1.2. The Interpreter Registers

The parameters of the Step method function as the registers of our virtual machine. Their specific modifiers are essential:

  • out object ans: The Answer Register. This is an output parameter used to return the final value of a computation.
  • ref IControl ctl: The Control Register. This reference parameter holds a pointer to the next computational object (`IControl`) to be executed.
  • ref IEnvironment env: The Environment Register. This reference parameter holds the context necessary for the execution of the control object, such as lexical variable bindings.

The use of reference (ref) and output (out) parameters is the key that allows a callee function to directly modify the state of its caller's execution loop, which is fundamental to achieving tail calls and other advanced control transfers.

1.3. The Four Modes of Control Transfer

A Step method executes its atomic portion of work and then relinquishes control in one of four distinct ways:

  1. Deeper Call: To obtain a required value, it can directly invoke the Step method of a callee function, initiating a deeper, nested computation.
  2. Value Return: It can conclude its computation by setting the ans parameter to its result value and returning false. The false return value signals to the caller that a value has been produced and normal execution can proceed.
  3. Tail Call: It can perform a tail call by setting the ctl parameter to the callee and the env parameter to the callee's required environment, and then returning true. The true return value signals to the caller's execution loop that it should not proceed, but instead immediately re-execute with the new ctl and env values.
  4. Unwind Participation: It can participate in a stack unwind event, a special protocol for capturing the continuation, which will be discussed in detail below.

2. The Trampoline: Enabling Tail Recursion

To avoid consuming the native call stack and prevent stack overflow exceptions during deep recursion, we employ a trampoline. This is a controlling loop that manages the execution of Step methods.

// Variables to hold the current state
IControl control = ...;
IEnvironment environment = ...;
object answer;
// The trampoline loop
while (control.Step(out answer, ref control, ref environment)) {}
// Execution continues here after a normal return (false)

The operation is as follows: When a callee wishes to tail call, it mutates the control and environment variables through the ref parameters and returns true. The while loop's condition evaluates to true, its (empty) body executes, and the loop condition is evaluated again, this time invoking the Step method on the newly specified control object. When a callee returns a value, it mutates the answer variable via the out parameter and returns false. This terminates the loop, and the ultimate value of the call is available in the answer variable.

3. The Unwind Protocol: Capturing the Continuation

The continuation is captured by hijacking the established return mechanism. This is a cooperative process that propagates upward from the point of capture.

3.1. Unwind Initiation

A special function (e.g., the primitive for `call/cc`) initiates the capture. It sets the answer register to a magic constant (e.g., `UNWIND`) and mutates the environment register to hold a new `UnwinderState` object, which will accumulate the stack frames. It then returns false, causing its immediate caller's trampoline to exit.

3.2. Unwind Participation and Propagation

Crucially, every call site must check for the unwind signal immediately after its trampoline loop terminates.

while (control.Step(out answer, ref control, ref environment)) { };
if (answer == MagicValues.UNWIND) {
    // An unwind is in progress. We must participate.

    // 1. Create a Frame object containing all necessary local state
    //    to resume this function from this point.
    Frame resumeFrame = new Frame(this.localState1, this.localState2, ...);

    // 2. Add the created frame to the list being accumulated.
    ((UnwinderState)environment).AddFrame(resumeFrame);

    // 3. Propagate the unwind to our own caller. Since this code is
    //    inside our own Step method, we have access to our caller's
    //    registers via our own parameters. We set *their* answer to UNWIND
    //    and *their* environment to the UnwinderState, and return false
    //    to drop *their* trampoline.
    return false; // Assuming 'ans' and 'env' are our own out/ref parameters.
}

This process creates a chain reaction. Each function up the conceptual call stack catches the unwind signal, preserves its own state in a Frame object, adds it to the list, and then triggers its own caller to unwind. This continues until the top-level dispatch loop is reached.

4. The Top-Level Dispatch Loop

The main entry point of the interpreter requires a master loop that can handle the three possible outcomes of an unwind event.

while (true) {
    answer = null;
    while (control.Step(out answer, ref control, ref environment)) { };

    if (answer == MagicValues.UNWIND) {
        UnwinderState unwindState = (UnwinderState)environment;

        // Outcome 3: The unwind was an instruction to exit the interpreter.
        if (unwindState.IsExit) {
            answer = unwindState.ExitValue;
            break;
        }
        else {
            // Outcome 1 & 2: A continuation was captured (cwcc) or is being invoked.
            // In either case, we must restore a control point.
            ControlPoint stateToRestore = unwindState.ToControlPoint();
            IControl receiver = unwindState.Receiver;

            // The RewindState holds the list of frames to be reloaded.
            environment = new RewindState(stateToRestore, receiver);
            control = ((RewindState)environment).PopFrame();
        }
    } else {
        // Normal termination of the entire program
        break;
    }
}
// Interpreter has exited.
return answer;

This top-level handler serves as the central arbiter. It runs the normal trampoline, but if an unwind reaches it, it inspects the UnwinderState to determine whether to exit the program entirely or to begin a rewind process to install a new (or previously captured) execution stack.

5. The Rewind Protocol: Restoring the Continuation

Invoking a continuation involves rebuilding the captured stack. This is managed by the `RewindState` environment and the `Step` methods of the captured `Frame` objects.

5.1. The `Frame` `Step` Method: A Dual Responsibility

The `Step` method for a `Frame` object being restored is complex. Its primary responsibility is to first restore the part of the stack that was deeper than itself. It does this by calling `PopFrame` on the `RewindState` to get the next frame and then running a local trampoline on it. The code that represents its own original pending computation is encapsulated in a separate `Continue` method.

// Simplified Step method for a Frame during rewind.
public override bool Step(out object answer, ref IControl control, ref IEnvironment environment)
{
    // First, set up and run a trampoline for the deeper part of the stack.
    object resultFromDeeperCall;
    IControl deeperFrame = ((RewindState)environment).PopFrame();
    IEnvironment rewindEnv = environment;
    while (deeperFrame.Step(out resultFromDeeperCall, ref deeperFrame, ref rewindEnv)) { };

    // Check if a NEW unwind occurred during the rewind of the deeper frame.
    if (resultFromDeeperCall == MagicValues.UNWIND) {
        // If so, we must participate again. Append our remaining frames to
        // the new UnwinderState and propagate the new unwind upwards.
        ((UnwinderState)rewindEnv).AppendContinuationFrames(this.myRemainingFrames);
        environment = rewindEnv;
        answer = MagicValues.UNWIND;
        return false;
    }

    // If the deeper call completed normally, now we can execute our own pending work.
    control = this.originalExpression;
    environment = this.originalEnvironment;
    return Continue(out answer, ref control, ref environment, resultFromDeeperCall);
}

This structure ensures that the stack is rebuilt in the correct order and that the system can gracefully handle a new continuation capture that occurs while a previous one is still being restored.

5.2. Terminating the Rewind: The `CWCCFrame`

The rewind chain must end. The innermost frame of a captured continuation corresponds to the `call/cc` primitive itself. Its `Step` method does not reload any deeper frames. Its sole purpose is to invoke the continuation receiver—the lambda function that was passed to `call/cc`—and provide it with the fully reified continuation object.

public override bool Step(out object answer, ref IControl control, ref IEnvironment environment)
{
    // The rewind is complete. Deliver the continuation to the waiting function.
    ControlPoint continuation = ((RewindState)environment).ControlPoint;
    return this.receiver.Call(out answer, ref control, ref environment, continuation);
}

With this final call, the stack is fully restored, the RewindState is discarded, and normal execution resumes within the receiver function, which now holds a reference to "the rest of the program" as a callable object.

by Joe Marshall (noreply@blogger.com) at Tuesday, October 28, 2025

Monday, October 27, 2025

Scheme Requests for Implementation

SRFI 250: Insertion-ordered hash tables

SRFI 250 is now in final status.

This SRFI defines an interface to hash tables, which are widely recognized as a fundamental data structure for a wide variety of applications. A hash table is a data structure that:

  • Is disjoint from all other types.
  • Provides a mapping from objects known as keys to corresponding objects known as values.
    • Keys may be any Scheme objects in some kinds of hash tables, but are restricted in other kinds.
    • Values may be any Scheme objects.
  • Provides an equality predicate which defines when a proposed key is the same as an existing key. No table may contain more than one value for a given key.
  • Provides a hash function which maps a candidate key into a non-negative exact integer.
  • Supports mutation as the primary means of setting the contents of a table.
  • Provides key lookup and destructive update in (expected) amortized constant time, provided that a satisfactory hash function is available.
  • Does not guarantee that whole-table operations work in the presence of concurrent mutation of the whole hash table. (Values may be safely mutated.)

Unlike the hash tables of SRFI 125, which is the direct ancestor of this specification, the hash tables described here are ordered by insertion: that is, associations inserted earlier in the history of the hash table appear earlier in the ordering. Advances in the implementations of hash tables, as provided by C++, Python, JavaScript, etc., make the provision of this new facility practical. As a result, the hash tables of this SRFI do not necessarily interoperate with the hash tables of SRFI 125, SRFI 126, or existing R6RS implementations.

by John Cowan and Daphne Preston-Kendal at Monday, October 27, 2025

Idiomdrottning

DSSSL with varargs

This is so jank but I was frustrated with how the rest parameter redundantly has all the keywords left in (unlike stragglers in define-options) so I did a grobian solution at least for now:

(define (strip-keys keys) (fn (strip-keys x keys)))

(define (strip-keys lis keys) (cons (car lis) (strip-keys (cdr lis) keys)))

(define (strip-keys (kw val . lis) keys)
  (strip-keys (require (member kw keys) lis) keys))

(define (strip-keys () keys) '())

And then here’s an example of it in use:

(define (lsrss #!rest files #!key (append #f))
  (set! files (strip-keys files '(#:append)))
  (...))

by Idiomdrottning (sandra.snan@idiomdrottning.org) at Monday, October 27, 2025

Wednesday, October 22, 2025

spritely.institute

Hoot 0.7.0 released!

We are excited to announce the release of Hoot 0.7.0! Hoot is a Scheme to WebAssembly compiler backend for Guile, as well as a general purpose WebAssembly toolchain. In other words, Scheme in the browser!

This release contains bug fixes and a few new features added since the 0.6.1 release back in May. Just in time for the upcoming Lisp Game Jam!

Make a game with Hoot

The 2025 edition of the Autumn Lisp Game Jam starts on Friday, 10/31! Halloween jam! 🎃

For those who are unfamiliar, the Lisp Game Jam is a casual, 10-day event where participants build small games using the Lisp implementation of their choice. We encourage you to join the jam and make a game with Hoot! Browser games are both easy to distribute and easy for other people to play, which makes Hoot a great choice! itch.io, where the jam is hosted, has built-in support for browser games.

To make it easy to jump right in and start jamming, we have a ready-to-use template repository on Codeberg that you can clone. It takes care of all the boilerplate and provides some useful bindings to essential web APIs like HTML5 Canvas.

Okay, onto the full release notes!

New features

  • Added Guile’s source module directory to Hoot’s load path by default. It is now easy to import any module from Guile that Hoot does not override in its core implementation. Of course, any Guile module that relies on a C extension or as-of-yet unsupported (guile) procedure/macro will not work with Hoot. Contributors are wanted to help maximize our Guile compatibility!

  • Removed stub SRFI-1 module in favor of loading it from Guile’s source module directory. Guile now has a pure Scheme implementation of SRFI-1 (no more C!) usable by Hoot.

  • Hoot modules can now be loaded from JavaScript typed arrays and ArrayBuffer objects in methods such as Scheme.load_main in reflect.js.

  • Top-level programs must now start with a use-modules or import form. The implicit import of (scheme base) caused more confusion for users than the convenience was worth.

  • The import form in a top-level program is now interpreted using R7RS syntax rules whereas previously it used R6RS syntax rules.

  • SRFI library names such as (srfi :1) are now canonicalized as (srfi srfi-1) in R6RS library forms to match Guile.

  • Added support for integer components in R7RS define-library names. Example: (define-library (srfi 1) ...).

  • Added support for #:renamer in define-module.

  • Added filter!, reverse!, and with-fluids* procedures to (guile).

  • Added printer for record type descriptors.

  • Documented HOOT_LOAD_PATH environment variable.

Performance improvements

  • Added optimized implementation of min and max.

Bug fixes

  • Explicitly set file permissions for reflection library files copied by guild compile-wasm --bundle. This problem was especially noticeable on Guix where the files were read-only, breaking successive bundle invocations.

  • Fixed hash to return the accumulated hash value when the recursion depth limit is reached. Previously, the result of hashv was returned, causing objects what were equal? to have different hash codes.

  • Fixed define-record-type to raise a syntax error when an invalid field is specified as a constructor argument.

  • Fixed R6RS rename syntax rules in library export forms.

  • Fixed implementation of #:replace in define-module syntax.

  • Fixed put-string which was not respecting the port argument.

  • Fixed docstring for ,hoot-run-file meta-command.

  • Fixed link to Git repository URL in the manual.

  • Replaced obsolete reference to parse-wat in the manual with wat->wasm.

  • Fixed compilation of unquote-splicing in non-tail position.

  • Fixed string-trim in the case where the result should be the empty string.

  • simple-format now returns the unspecified value rather than zero values.

  • Fixed select instruction handling in Wasm toolchain.

Browser compatibility

  • Compatible with Safari 26! 🎉 At long last, Hoot modules now work reliably in Safari and other sufficiently fresh WebKit-based browsers.

  • Compatible with Firefox 121 or later.

  • Compatible with Chrome 119 or later.

Get Hoot 0.7.0

Hoot is already available in GNU Guix:

$ guix pull
$ guix install guile-next guile-hoot

(Hoot currently requires a bleeding-edge version of Guile, hence guile-next above.)

Otherwise, Hoot can be built from source via our release tarball. See the Hoot homepage for a download link and GPG signature.

Documentation for Hoot 0.7.0, including build instructions, can be found here.

Get in touch

For bug reports, pull requests, or just to follow along with development, check out the Hoot project on Codeberg.

If you build something cool with Hoot (for the upcoming game jam or otherwise), let us know on our community forum!

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
  • Daniel Finlay
  • David Anderson
  • Holmes Wilson
  • Lassi Kiuru

Gold tier

  • Juan Lizarraga Cubillos

Silver tier

  • Adam King
  • Austin Robinson
  • Brian Neltner
  • Brit Butler
  • Charlie McMackin
  • Dan Connolly
  • Danny OBrien
  • Deb Nicholson
  • Eric Schultz
  • Evangelo Stavro Prodromou
  • Evgeni Ku
  • Glenn Thompson
  • James Luke
  • Jonathan Frederickson
  • Joshua Simmons
  • Justin Sheehy
  • Michel Lind
  • Mikayla Maki
  • Mike Ledoux
  • Nathan TeBlunthuis
  • Nia Bickford
  • Noah Beasley
  • Shane Redman
  • Steve Sprang
  • Travis Smith
  • Travis Vachon

Bronze tier

  • Alan Zimmerman
  • Alex Poylisher
  • BJ Bolender
  • Ben Hamill
  • Benjamin Grimm-Lebsanft
  • Brooke Vibber
  • Brooklyn Zelenka
  • Carl A
  • Crazypedia No
  • François Joulaud
  • Grant Gould
  • Gregory Buhtz
  • Ivan Sagalaev
  • Jason Wodicka
  • Jeff Forcier
  • Louis Jackman
  • Marty McGuire
  • Mason DeVries
  • Neil Brudnak
  • Nelson Pavlosky
  • Philipp Nassua
  • Robin Heggelund Hansen
  • Rodion Goritskov
  • Ron Welch
  • Stephen Herrick
  • Steven De Herdt
  • Sébastien Rey-Coyrehourcq
  • Tamara Schmitz
  • Thomas Talbot
  • William Murphy
  • r g
  • terra tauri

Until next time, happy hooting! 🦉

by Dave Thompson (contact@spritely.institute) at Wednesday, October 22, 2025

Tuesday, October 21, 2025

Idiomdrottning

Chatbot the ultimate social media feed

coffeetree: the chatbot paradigm is literally an infinite feed
baconlad: hard disagree on this. Its not feeding you content.
coffeetree: That’s like saying “I only use this one tiny feature of Facebook, and no other features, and no other platforms, and I handle that paradigm just fine.”

And some people do say that. I think both sides are right here in that yes it is a useful tool and you get to avoid a lot of the bad parts of the web, but also, it is addictive and like how many silo social media sites it adapts to you to keep you hooked, and also, the environmental and economic impact is huge. The increased wealth gaps and increased concentration of ownership of means of production is an undeniable ginormous downside of the current slate of LLM and ANN tech. Our economy was already a broken buggy system even before the gigaton wrench thrown into it by ML. 😭

They in some very real semiotic senses are different, yes. But the similarites are that they are these huge data center machines that take all the input from what people post online and chew it up and push it back to you in a way that they control and can profit from. AI is in someways even worse, an even more purely distilled version of the idea of The Algorithm.

Now, that’s social media in the sense of algorithmic silo socials like TikTok, X, Tumblr, Facebook, Instagram, YouTube and similar. Not all social media is algorithmic (Discord isn’t, although it is a silo) nor is all social media even silos; email and fedi are examples of non-silo socials.

And by “algorithm” we mean specifically recommendation/discovery algorithm; obviously all computer stuff uses running code i.e. “algorithms” in a much broader and gentler sense. Even a 1980s BBS “had algorithm” since it used computer programs to work but in a more specific sense of “algorithm”, we mean the reco engine. The hideous world-wrecking reco engine. And AI chatbots keep that part, the bad part. They just remove the part of it that were your friends.

The comparison of “chatbot as the ultimate social media feed” (in a negative sense) is stretched but insightful and I appreciated it.

by Idiomdrottning (sandra.snan@idiomdrottning.org) at Tuesday, October 21, 2025

Lite irrtankar om Delta Chat

Jag använder Delta Chat med min egen mejlserver och kopplat till min vanliga mejladdress så jag kan också “chatta” med vilken mejladdress som helst på hela jorden utan att behöva försöka övertala dom att installera samma chatapp som mig; dom använder bara sin vanliga mejlapp. Mejl från myndigheter, föräldrar, randos online; allt dyker upp som chatmess och jag kan svara direkt på ett ursmidigt sätt och få överblick över tidigare meddelanden i samma tråd bara genom att scrolla upp. Och behöver jag nåt mer fancy får jag kavla upp ärmarna och starta den vanliga mejlappen jag också har installerta och kopplat till samma konto.

Det går inte om man har en chatmailserver för att dom har stängt av alla vanliga mejl ut, i nån sorts försöka att minska spam (men det går ju fortfarande att spamma andra deltachatanvändare så jag är inte helt nöjd med hur dom tänkte där).

Jag hoppas Delta Chat fortsätter värna om kompabiliten med vanlig email och inte lägger alla ägg i sin chatmailkampanj. Idén med att koppla ihop sig via chatmailservrar har inga egentliga fördelar gentemot ex vis XMPP/OMEMO bara att det finns ännu färre servrar och ännu färre användare och man ramlar i det klassiska nätverkseffektsproblemet att för dom flesta av oss går det inte att Oövertala släkt & vänner att joina ytterligare en app och vice versa, att bli övertalad till det, är ännu tristare.

Det som är fint med att det är email är ju att det går att chatta med alla så länge dom har en mejladdress och valfri mejlapp. Ja, jo, en del personer har en aversion mot mejl och och kommer vägra svara en och säga att mejl är nåt dom bara har för brev från mormor, skatteverket, och återställa lösneordet (och dom har jag ju försökt tipsa om Delta Chat men en del vill inte det, se nätverkseffektsproblemet ovan) men bortsett från dom. Det gör det dessutom bra om båda sitter på ex vis gmail och deltachattar med varandra och nån vill stoppa den konverslationen så vad ska dom göra? Dom behöver stänga ner hela Google. Det går alltså att “snylta” på den vanliga epostinfrastrukturen och ha det till chattande.

Delta Chat stöder läsning av alla sorters PGP men för att skicka ut med e2ee-kryptering går det bara om den du pratar med använder Autocrypt och du fått minst ett Autocrypt-mejl från dom innan. Det gör också att chatmails “vi raderar automatiskt alla icke-e2ee–mejl” policy blir lite awkward för innan så var det ju bara att börja med lite hej hej och sen blev det krypterat från och med det.

Ex vis hade K9 stöd för Autocrypt, inte från början men det fanns en inställning (misstänker att Mozilla kommer att ta bort det för dom är fiender till Autocrypt och dom tog bort det ur Thunderbird). Posteo har också det, dom kan lägga till Autocrypt-headers automatiskt på alla utgående mejl när du väl lagt in din nyckel. Men för chatmailanvändare betyder det i båda fallen att den andra personen måste skicka första mejlet så du får deras nyckel. Och när det är chatmail till chatmail som ska börja prata måste dom alltså numera ha nån sorts mellanlager för att starta kontakten. 🤦🏻‍♀️

Därför hoppas jag Delta Chat fortsätter att låta chatmail vara frivilligt så vi som använder det som en vanlig mejlapp kan fortsätta med det. För dom som vägrar PGP blir det ändå s2se vilket är betydligt bättre än ex vis SMS. (Det är så frustrerande när folk på ex vis arbetsförmedlingen säger “vi får inte skicka tider över mejl för det är så osäkert, det är bäst att vi SMS:ar det istället”, det är galet, SMS är en miljon miljoner gånger osäkrare än normal jäkla TLS-epost vilket all epost numera är.

Delta Chats fokus på enbart Autocrypt är ofta lite frustrerande. Om jag får ett vanligt okrypterat mejl är det bara att svara (för mig som inte är fast på chatmail alltså utan fortfarande har det kopplat till en normal mejl) och om jag får ett mejl med Autocrypt är det också bara att svara, men om jag får ett mejl med PGP utan Autocrypt, då måste jag bita ihop och rulla upp den andra mer robusta men också mkt mkt osmidigare mejlappen.

Det gäller då dels nördar som handpillar med PGP som om det vore nittiotal, vilket är ett fåtal men jag får ändå några såna mejl i veckan, och dels dom som använder den stora konkurrenten till Autocrypt, nämligen WKD. Och det betyder Proton! Eftersom min hemmasnickrade mejlserver har slagit på både Autocript och WKD (vilket Posteo också numera har fast varje användare måste slå på det manuellt), betyder det att jag kommer få krypterade mejl automatiskt från varenda Protonanvändare. Och jag kan läsa dom i Delta Chat men måste som sagt komma ihåg att svara från min mer robusta meljapp. (Som heter Emacs.)

WKD är mycket bättre än Autocrypt för det funkar från första mejlet eftersom det är helt out of band och det är också mindre sårbart för MITM. Autocrypt har dock en stor stor fördel gentemot WKD och det är att det funkar med alla mejlservrar. Du kan sätta Autocrypt på dina gmail-mejl men det kommer aldrig gå att sätta WKD på dom om inte Google plötsligt själva bestämmer sig för att stödja det eller om du köper den där grejen där du får din egen domän på gmailen.

Jag har ändå tidigare föreslagit att Delta Chat ska stödja både WKD och Autocrypt så att så fort jag får ett mejl från nån som har WKD så ska jag kunna chatta krypterat med dom änven om dom inte har Autocrypt. Och nu med chatmail-kampanjen när man ändå behöver speciella servrar så gäller det ju dubbelt för varför har då inte chatmail-servrarna automatiskt WKD också!? Då skulle det funka sömlöst med Proton! (Tuta kan dra åt helvete men det vet förhoppningsvis alla vid det här laget.)

Anledningen till att dom drog igång chatmailkampanjen var gissnigsvis två anledningar. Dels att Hotmail blev inkompatiblet med Delta Chat (alltså jag kan fortfarande chatta s2se med hotmailanvändare men dom kan på sin sida inte köra Delta Chat) så min polare som använde Delta Chat med sin hotmailaddress fick byta till att göra det med gmail och det kanske inte kommer funka så länge till. Den andra tror jag är att om man blandar Delta Chat med mejl men vill bara ha Delta Chat till chattar och bara vanlig mejl till vanliga mejl så kommer chattarna poppa upp tillfälligt i den vanliga meljappen och sen försvinna vilket kan uppfattas som störigt. Medan jag som får och vill ha alla mejlen i båda apparna (med olika blocklistor, ex vis jag har blockat alla nyhetsbrev och sånt från Delta Chat för jag läser dom genom den vanliga meljappen istället), jag hade inte det problemet. Men visst finns det buggar med min blandsetup. Delta Chat har nyligen infört en misfeature att det inte längre går att svara på mejlinglistemejl. Förut var det väldigt smidigt när nån på GitHub kommenterade på en PR så kunde jag svara direkt i Delta Chat men det har dom numera alltså slängt av vilket är otroligt ogint och onödigt. Read Only grejer i Delta Chat är det sämsta av båda världar eftersom det är en mycket dålig läsare + jag vill inte bli pingad av read-only saker utan vill läsa dom nån gång i veckan i lugn och ro + hela uspen med Delta Chat är ju hur smidigt det går att svara, som på en chatt för det är en chat!

Men sen ja deras implementation av PGP är väldigt robust. Min kompis som bytte sin Delta Chat från hotmail till gmail? Han är den enda som jag inte kan mejla från min vanliga mejlapp eftersom den inte stöder PGP-nycklar där epostadressen inte matchar vilket hans inte längre gör. Nu när min älskade tablet är på lagning använder jag den vanliga mejlappen över SSH från en laggig och seg e-ink–skrivmaskin och alla andra Delta Chat–polare kan jag skriva till men inte honom.

Och ja det går att ha stickers♥︎

Det går till och med att ha speciell appar som man skickar till varandra som typ zipfiler eller vad det är. Det heter webxdc. Jag bortsåg glatt från detta först för jag vill inte spela liksom spel och sånt i min mejl men sen hittade jag en jag gillar väldigt mycket och gärna använder ihop med en av mina Delta Chat-kompisar, en kalender som jag tycker är bra. Faast det var inte helt smart nu när jag för tillfället är hänvisad till att åter igen använda min vanliga mejl för att ha kontankt med honom och alla andra tills min tablet blir lagad.

by Idiomdrottning (sandra.snan@idiomdrottning.org) at Tuesday, October 21, 2025