When I was younger, I played a lot of MUDs (“Multi-User Dungeons” — the text-only predecessors of modern MMORPGs, often played over Telnet). They were great fun, particularly during high school: a lightweight multiplayer game with no client state meant you could log in from any machine in any lab, even Windows shipped a Telnet client in those days, the Telnet protocol was light enough to run on my school’s slow PCs and limited internet connection, and the lack of flashy graphics meant it was easy to hide the window from a passing teacher or librarian.
At some point, building and tinkering with MUDs became more
interesting than playing them. In those days, MUD builders and wizards
(admins) were often recruited from each game’s playerbase, and many MUDs
let builders edit the world through in-game commands. This was
incredibly cool at the time — even through a clumsy line-oriented (ed
-style)
editor, there was something magical about summoning blank rooms from the
void, writing rich descriptions to turn them into “real” spaces, and
adding items and “mobs” (Mobile OBJects — NPCs) to make them come to
life. A few of my friends and I signed up to a “builder academy” MUD,
where everyone got a zone to mess around in, and we tried our hand at
crafting our own areas. Most of these projects didn’t get very far, and
all of them have been lost to time.
There’s only so much you can do with builder rights on someone else’s MUD. To really change the game, you needed to be able to code, and most MUDs were written “real languages” like C. We’d managed to get a copy of Visual C++ 6 and the CircleMUD source code, and started messing about. But the development cycle was pretty frustrating — for every change, you had to recompile the server, shut it down (dropping everyone’s connections), bring it back up, and wait for everyone to log back in.
Some MUDs used a very cool trick to avoid this, called “copyover” or “hotboot”. It’s an idiom that lets a stateful server replace itself while retaining its PID and open connections. It seemed like magic back then: you recompiled the server, sent the right command, everything froze for a few seconds, and (if you were lucky) it came back to life running the latest code. The trick is simple but I can’t find a detailed write-up, so I wanted to write it out while I thought of it.
Read more...I’ve been very impressed by the Cosmopolitan Libc project
and the αcτµαlly pδrταblε
εxεcµταblε file format that makes it all possible. Building a crazy
polyglot binary that works on two architectures and half-a-dozen
operating systems is one thing, but Mozilla’s Llamafile
project showed that APEs were more than just a cool hack: it’s stunning
that I just download a reasonably smart LLM to my computer,
chmod +x
it, and begin a conversation. LLM use-cases aside,
single-file dependency-free programs that work basically anywhere are
just a great thing to be able to stick on a USB stick.
But APEs have been a little finicky for me. I run NixOS as my primary operating system, and
sometimes an APE will refuse to launch, or I’ll need to pass
--assimilate
to it and permanently turn it into an x86-64
Linux binary before it’ll work.
The Cosmopolitan project, of course, has a couple of answers ready.
One is to install a system-wide APE loader separate from any
one binary and register it with binfmt_misc
;
the other is to patch the
kernel so it can natively detect APEs. I don’t want to wait for a
full kernel compile each time I need to update, so until the patch lands
in mainline binfmt_misc
is the way to go.
I have set up a Nix Flake at https://git.sr.ht/~jack/cosmo.nix
to hold my experiments joining the Nix and Cosmopolitan universes. For
now, it has convenient NixOS options to register the APE loader with
binfmt_misc
:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
# Import cosmo.nix and plumb our nixpkgs rev through it
cosmo-nix = {
url = "sourcehut:~jack/cosmo.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs@{ nixpkgs }: {
nixosConfigurations.myMachine = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# Include the cosmo.nix module...
inputs.cosmo-nix.nixosModules.default
# ...and register the ape-loader with binfmt_misc
{ programs.ape-loader.binfmt = true; }
# Your other NixOS config here.
];
}
}
}
Not bad for an evening’s hacking! I hope to take it further at some
point in the future: I’d like to build the loader itself from a known
bootstrap path rather than fetching a binary, and I’d especially love to
be able to do the same with Cosmopolitan Libc and cosmocc
. I’m
a big fan of bootstrappable
builds, and reliable building of ultraportable binaries from a
documented bootstrap path would be an incredibly cool achievement. I
currently get so much useful stuff out of nix develop
— all
my build dependencies, pre-commit hooks,
useful CI targets, and more — and I have projects in mind where I want
Cosmopolitan Libc to be a “tier 1” platform. Nix-based access to a
cosmocc
toolchain and all cosmocc
-built build
dependencies would make that so much easier.
Haskell’s expressive type system means that type signatures can carry
a lot of information. Haskell’s polymorphism means that you sometimes
write functions that work across an enormous range of types, and are
left wondering “what do I actually call my variables?”. It is often the
case that there’s nothing to say beyond “this variable is a Functor
”,
or “this variable is a monadic action”, and so a single-letter variable
name is appropriate. An unofficial and largely undocumented convention
has emerged around these variable names, and so I wanted to write them
all down in one place.
It should go without saying that single-letter variable names are not always the answer. Like point-free style, it sometimes obscures more than it helps and people get carried away with it. But when you have a highly polymorphic function and no good words to use, choosing the right letter can convey a surprising amount of meaning.
This dictionary is not and cannot be exhaustive. Variable naming often relies on context to convey information, and shorter variable names should only be used when they make sense in context. That context could be:
(...) =>
context to reference
“nearby” concepts, as in Monoid
and
Monad
;(k, v)
to
reference the key and value of a Map
entry; orposition a u t = u * t + (a * t * t) / 2
is reasonable if
the reader knows you’re talking classical mechanics.With the warnings out of the way, the dictionary is after the jump. The bulk of the dictionary documents type variables, where overly long variable names can blow out complicated type signatures. Important value-level variable names are also documented, and are explicitly labelled as such.
Read more...I have travelled a lot this year, and after yet another trip where I lugged too many things around, I’ve been thinking about ways to cut back. The classic guide for this is onebag.com, which covers a very interesting mix of techniques and some carefully-chosen lightweight gear that will take you to the farthest corners of the map. Perma-nomad Vitalik Buterin has his own take on living out of a 40L backpack; one of his key points is to run everything you can off of USB-C. The benefits should be obvious: you cut down the number of charging cables you need to carry and your power bank can recharge any of your devices. While I’m a bit of a luddite, I can see a lot of people travelling with at least a laptop and phone, and possibly also a tablet, earbuds, and/or a smartwatch, all of which need power.
Vitalik’s guide mentions a USB-C “wall wart” charger that he uses to power his stuff. I disagree with this choice, at least for international travel: I think you want a desktop USB-C charger that takes an IEC C7 (“figure 8”) cable.
I’m not aware of any good articles that spell out the “desktop charger + replaceable cable” trick and how to actually find a suitable one, so a full explanation, research procedure, and some tentative recommendations are spelled out in laborious detail after the jump.
Read more...I’ve been meaning to write this one for a while, but the announcement of the Ladybird Browser Initiative makes now a particularly good time.
TL;DR: Chrome is eating the web. I have wanted to help fund a serious alternative browser for quite some time, and while Firefox remains the largest potential alternative, Mozilla has never let me. Since I can’t fund Firefox, I’m going to show there’s money in user-funded web browsers by funding Ladybird instead. You should too.
Read more...