MacPaint Art From The Mid-80s Still Looks Great Today
This 1-bit art is so good!
This 1-bit art is so good!
I could use this backend-agnostic script. Right now I just spam Ctrl-R!
Python-based parametric CAD modeling software.
There seem to be a lot of nice features. It seems more like Fusion360 than OpenSCAD does, in that you create 2D sketches and then extrude or revolve them into 3D models.
See: Awesome build123d
I've been trying to decide if I want to move my small dynamic websites to a VPS from Heroku or just use simple PHP and basic web hosting. My main problem with VPS is I don't want to deal with maintaining it. Simple monitoring like updown.io or Uptime Robot hooked up to email could help.
You can add notes to commits, branches, and all kinds of things in git. The only problem is they aren't easy to view on anything except commits via git log
. GitHub doesn't show them at all, but I think Forgejo/codeberg.org does show them and let you add/edit them (see PR4753 and PR6025).
stack overflow: how to push git notes to a central server:
git push origin 'refs/notes/*'
and
git fetch origin 'refs/notes/*:refs/notes/*'
See also the git website doc on git notes
This post points out that it's often difficult to tell what a function does when passed true (or false). The given example of the ubiquitous "filter" function is great; does true or false make you keep a list element? Renaming filter to "select" and/or "discard" makes the boolean clearer.
The author points out that substituting a function that returns the Haskell Maybe type makes makes more sense in some "filter" scenarios.
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
builds a list with only entries where the given function returns Just b
See Data.Maybe.mapMaybe for lists and the more general Data.Witherable.mapMaybe.
Related: the wrong abstraction
Modules for Experiments in Stellar Astrophysics (MESA) is an advanced, open-source 1D stellar evolution code. The code is developed and maintained by a globally distributed team of researchers. MESA is written primarily in Fortran with a modular, flexible design that facilitates easy interfacing.
It seems to be for both research and teaching.
Found in /proc/pressure/
are cpu
, memory
, and io
, text files that contain info on the fraction of processes "stalled" due to contention for the given resource.
Other links:
To maximise something which grows geometrically, choose the course of action that maximises the expected log-wealth of the thing.
The author wrote a related post When is insurance worth it? that I enjoyed as well.
RANLUX is highly regarded in the particle physics simulation community, but as far as I remember, we used the Mersene Twister in the Compact Muon Solenoid experiment. It must have been because it was faster.
The linked post is an effort to modernize RANLUX and make it faster.
That is: generative AI works the same way as gambling addiction. Spin the gacha! Just one more spin, bro, I can feel it!
Large language models work the same way as a carnival psychic. Chatbots look smart by the Barnum Effect — which is where you read what’s actually a generic statement about people and you take it as being personally about you. The only intelligence there is yours.
Tutorials and instructions usually recommend dd
over cp
for copying disc images to devices (like USB sticks). It seems that this is mostly superstition.
The main benefit is that dd
lets you specify the block size used for copying, but cp
automatically selects the block size at least as well as I would.
The other benefit of dd
is a progress display. Piping the output of the pv
command to the destination disc may be a better option.
A "typeface for visual impairment" that even includes mathematical symbols. It seems to be developed by some groups in France and Switzerland.
A hawk learning that an audio pedestrian walk signal at a traffic light leads to backed up cars it can use as cover to approach prey.
The published paper is here: https://doi.org/10.3389/fetho.2025.1539103
An evolution of Artsey, with the addition of larger key layouts.
An Easy, Fast, Open One-Handed Keyboard System
The layout diagrams are really nice!
Adding an enum datatype can simplify code. Don't be afraid to add module-internal ones.
rr aspires to be your primary C/C++ debugging tool for Linux, replacing — well, enhancing — gdb. You record a failure once, then debug the recording, deterministically, as many times as you want. The same execution is replayed every time.
This seems like it could be really useful, especially for intermittent bugs e.g. race conditions.
Continuously stream SQLite changes to AWS S3, Azure Blob Storage, Google Cloud Storage, SFTP, or NFS. Quickly recover to the point of failure if your server goes down.
If pushing responsibility forward means accepting whatever parameters and having the caller of the code handle possibility of failure, then pushing it back is going to mean we accept stricter parameters that we can’t fail with.
An example of the former is:
uncons :: [a] -> Maybe (a, [a])
and of the latter is:
head :: NonEmpty a -> a
Working with the latter pushes error handling to the data input code and cleans up all the rest.
A really nice explanation of how to use SystemD timers to replace cron.
Keep as much logic in the database, using constraints, stored procedures, and functions (Postgres even lets you write them in Python) as possible.
This reduces duplication of logic between the DB and external code, and allows the external code to change without issue.
I never really have figured this out before. Here is a way:
grep -l pattern | xargs sed -ri 's|pat(tern)|\1s are birds|g'
The -l option to grep (and ripgrep) makes it just output filenames containing matches. The sed option -r enables extended regex (-E may alternatively be used like in grep), and -i replaces in place.
If any filenames contain spaces, newlines, or quotes, then xargs can get messed up. Terminating lines with null characters fixes that:
grep -lZ pattern | xargs -0 sed -ri 's|pat(tern)|\1s are birds|g'
or
rg -l0 pattern | xargs -0 sed -ri 's|pat(tern)|\1s are birds|g'
A story about using Linux blind. It makes me sad that accessibility in Linux has severely declined over the last 15 or so years!
Intuitive to use version of the find program.
fdfind -e py # finds all Python files in current directory
fdfind pattern -t d # finds all directories matching pattern
ncurses-based disk usage program. It seems fast and easy to use.
watchexec can rerun a command when a set of files is modified and is designed for ease of use. Two examples from the README are:
$ watchexec -e js,css,html npm run build
$ watchexec -r -e py -- python server.py
Frustratingly, it's not a Debian package. Installation with cargo install --locked watchexec-cli
isn't hard, but you don't have much control over 300+ dependencies.
Using custom types wherever there is some concept of validity can help make a codebase free from checking validity everywhere. It only must be done in the constructor and errors handled by the caller of the constructor.
So look through a codebase for functions that take string, int, and float!
"A short guide to LaTeX that avoids legacy cruft."
An open-hardware microscope. Make with 3D-printed parts!
Paper about 3D printers applied to microscopy. Some interesting projects and observations about 3D printing.
This lets you run containers using systemd, rather than having a complex, resource-hungry Kubernetes or Docker daemon manage them.
Podman is also capable of updating containers automatically, so the combination is something like Heroku.
"Design is figuring out how to find a point in the middle."
You cannot make an abstraction more powerful without sacrificing some properties that you used to know about it. Necessarily. You cannot require a new property be true about an abstraction without sacrificing some of its power and flexibility. Always.
The mistake is to forget this. Nearly always, this error happens in one direction. To look on a design, see what cannot be done with it, and attempt to “fix” it. To make it more powerful, and forget that it necessarily becomes more meaningless.
A bunch of things to note in designing a type system to make error messages better. I think the examples of bad error messages in languages being due to the type system (C++!) are broadly interesting.
Type annotations affect runtime behavior in statically compiled languages, but not ones where types are bolted-on after the fact (Python).
This makes runtime behavior depend on the type inference algos and can be difficult to reason about.
The example in Haskell is wild!
A nice tutorial on PDO, the built-in SQL interface for PHP.
In particular, the official documentation doesn't say much about querying, but it's covered well here.
The other pages on the site are good too.
A static analyzer with (optionally very strict) type checking. Developed at Vimeo.
Includes a language server (link to docs)
Declarative tool for creating virtual machine images. This way you can bake all of the installed software into an image, rather than having to provision once a virtual machine boots.
This plus it's parent Terraform (open source version: OpenTofu) seem to be the best way to do infrastructure as code, at least at a certain scale.
A similar tool, for bare metal, is goldboot
Think
ansible
but Python instead of YAML, and a lot faster.
This seems more up my alley than ansible.
For a server or two, I'm not sure if this is the way to go or something more complicated like Packer
A guide to writing property-based tests. The author, John Hughes, co-wrote QuickCheck in Haskell, the first package for property-based testing.
Published conference paper: http://dx.doi.org/10.1007/978-3-030-47147-7_4
Some quotes:
"Avoid replicating your code in your tests," because it's lot of work and likely to contain the same errors as your code.
"Test your tests," because if your generator and shrinker produce invalid values, everything else will fail too.
Validity testing consists of defining a function to check the invariants of your datatypes, writing properties to test that your generators and shrinkers only produce valid results, and writing a property for each function under test that performs a single random call, and checks that the return value is valid.
A postcondition tests a single function, calling it with random arguments, and checking an expected relationship between its arguments and its result.
A metamorphic property tests a single function by making (usually) two related calls, and checking the expected relationship between the two results.
Inductive properties relate a call of the function-under-test to calls with smaller arguments. A set of inductive properties covering all possible cases together test the base case(s) and induction step(s) of an inductive proof-of-correctness. If all the properties hold, then we know the function is correct–inductive properties together make up a complete test.
A model-based property tests a single function by making a single call, and comparing its result to the result of a related “abstract operation” applied to related abstract arguments. An abstraction functions maps the real, concrete arguments and results to abstract values, which we also call the “model”.
A Python profiler that profiles unmodified Python processes from its own process. It can profile production code with little performance impact. It profiles extensions and all threads, which the standard library cprofile can't do.
I've needed to do this countless times in particle physics data analysis and other areas.
What I've done before:
data = {'a' : ['alpha', "beta"], 'b' : ["alpha"]}
result = {}
for key in data:
for value in data[key]:
try:
result[value].append(key)
except KeyError:
result[value] = [key]
and the simpler solutions suggested in the link:
data = {'a' : ['alpha', "beta"], 'b' : ["alpha"]}
result = {}
for key in data:
for value in data[key]:
result.setdefault(value,[]).append(key)
or
from collections import defaultdict
data = {'a' : ['alpha', "beta"], 'b' : ["alpha"]}
result = defaultdict(list)
for key in data:
for value in data[key]:
result[value].append(key)
dict.setdefault
and collections.defaultdict
are new to me and useful!
The idea is to just set members in object constructors to keep them simple. Any complex data conversion, or side-effects should be moved to a class-method.
This seems like a great way to deal with constructors taking multiple different inputs.
I do think using traditional constructors has the advantage of following a widely used convention (the principle of least surprise in API design) and can be simpler to use.
Makes configuring SSL simple by enabling a whole set of security policy directives in one command.
Apache includes policies from the Mozilla organization, including the most secure option: "modern"
Apache's built-in mod_md can handle automatic retrieval and renewal of Let's Encrypt certificates (or similar with ACME) without external software. Convenient!
A web-app for drawing ASCII diagrams.
Using the time of flight of relativistic cosmic ray muons to estimate relative position.
There is also a 2024 paper on a more practical, wireless system and another on time synchronization from 2022.
Here is the author, Hiroyuki K.M. Tanaka's inspirehep page.
A more modern, but mostly compatible, version of Vim.
After trying Kakoune and Helix, I realized it would be too much work to re-learn a bunch of key-bindings and ways of doing things. I'm not ready to put the effort into a project that isn't very far along. With Neovim, I can always go back to Vim if the project fails.
I've been messing with configuring Neovim for a few days now, and it can be intimidating when you see a bunch of code to configure something in a web search result. I've found my way around that, and have things mostly up and running.
Overall, I think I'm happy to use Neovim for now.
The tldr pages are a community effort to simplify the beloved man pages with practical examples.
They are a really nice set of examples. I especially like the ones for git commands, like git rebase.
2025-04-10 Update: I tried Helix for a few days, and like it, but miss features from Vim and want something more complete. Maybe I'll try to get LSP and completion working in Vim or give NeoVim a shot!
Helix is a Vim and Kakoune inspired editor. It follows Kakoune's philosophy of multiple selections and redesigned keybindings, but brings back Vim's visual mode. It is written in Rust and tries to include what most users would include through Vim plugins, e.g. tree-sitter and language server support (LSP). It doesn't currently have plugin support.
I'm writing this post in Helix. It's nice to have more of the "batteries-included" features like tiling window support and a file manager. I haven't figured out the tree-sitter/LSP integration yet.
A few things are missing, like spell checking (maybe can use a language server?) There does seem to be Git support. It's a nice little colored line between the line numbers and the text. It doesn't work the same as Vim-Fugitive, though, as it only shows the diff from HEAD, not from staged.
This technique lets you boot your broken installation with a live USB disc, then use chroot to switch the running Linux over to the broken installation. You can then run package manager commands, etc.
arch-chroot is a script that does all of the steps for you and is even available on Debian.
systemd-nspawn can be used in a similar way:
systemd-nspawn --directory /tmp/target-rescue --boot -- --unit rescue.target
It is usually used as a light weight host virtualization technique, and is new to me!
Chart for shape and size of light bulbs. A19 is the standard size.
They also have a base chart here. Medium/26mm/E26 is the standard one that goes with A19.
I've just started reading the long-running webcomic "Freefall." I particularly like this joke about "quark" ice cream:
I thought everyone liked "quark" ice cream. I've got all three colors in all six flavors.
Author: Dan McKinley
The idea is that the more new, exciting, "poorly-understood" things you choose, the more complex and difficult to deal with your system becomes. He advocates for a company to pick three or less non-boring technologies and to pick them where it maximizes your competitive advantage.
This whole article is great!
What counts as boring? That’s a little tricky. “Boring” should not be conflated with “bad.” There is technology out there that is both boring and bad (we often casually refer to the boring/bad intersection of doom as “enterprise software,” but that terminology may be imprecise). You should not use any of that. But there are many choices of technology that are boring and good, or at least good enough. MySQL is boring. Postgres is boring. PHP is boring. Python is boring. Memcached is boring. Squid is boring. Cron is boring.
The nice thing about boringness (so constrained) is that the capabilities of these things are well understood. But more importantly, their failure modes are well understood.
Emphasis theirs:
One of the most worthwhile exercises I recommend here is to consider how you would solve your immediate problem without adding anything new. First, posing this question should detect the situation where the “problem” is that someone really wants to use the technology. If that is the case, you should immediately abort.
Counterintuitively, Python's range function isn't an iterator, because you can't call next() on it. It is lazy like iterators but is more like a "lazy collection." Like a collection, you can make an iterator from one with the iter() function.
A good sign of an abstraction, e.g function, that needs to be replaced by copy-pasted code is one with boolean flag parameters. It's probably better to "inline" the code back to the call site, and then see what parts of it, if any, are still shared and make a new abstraction.
This simple circuit directly converts from RF down to baseband. Four square waves operating at and shifted 90° from each other control a multiplexer thus producing four quadrature signals. These can be combined to give I and Q signals. A copy of the PDF is here.
I found this while reading about the Pi Pico Rx, which uses this circuit and a Raspberry Pi Pico to make a software defined radio.
To only perform a monadic or applicative action if a value exists, you can use
$ action for_ value
So for example, if x
is a Maybe String, you could print it if it exists with:
$ putStrLn x for_ x
I think that's the beauty of Haskell, this thing with a name from lists, when generalized to (Traversable) Applicatives, can do useful things for all kinds of things.
Since for_
is just traverse_
with it's arguments flipped, you could also do:
traverse_ (action) value
Keep in mind for_
and traverse_
are in Data.Foldable
and not present in the Prelude.
Also, oddly, is the linked blog post scheduled to be published in the future?
*> action -- Perform action if condition is true else fail
guard condition $> value -- Return value if condition is true else fail
guard condition <$ guard condition -- Like the 1st line, but switched around value
This can effectively make sure a condition is true before doing something or returning a value.
Keep in mind "failure" can mean lots of things depending on the surrounding Applicative. It could mean returning Nothing, or raising an exception.
You will need the following imports to do this:
import Control.Mondad (guard)
import Data.Functor ((*>),($>),(<$))
How do you test code against a different version of libc than the system default one? You can't reliably use an alternate libc by putting it in LD_LIBRARY_PATH
. It will often crash.
You might think that the kernel is somehow responsible for dealing with dynamic libraries, but that’s not the case. Dynamic libraries are a user-space concept and this is precisely where the [ELF] interpreter [ld-linux.so] comes into play.
To summarize, glibc ships both the ld-linux.so dynamic linker and the libc.so.6 shared library. The two are closely coupled. Running a binary against the C library without also using a matching dynamic linker can lead to crashes. But running a binary directly with a specific dynamic linker ensures that the binary picks up the matching C library.
At compile time, you can include the interpreter ld-linux with:
bash gcc -o myexe -Wl,--dynamic-linker=/wherever/ld-linux-x86-64.so.2 mycode.c
At runtime, you can use patchelf to replace the reference to the interpreter in the binary:
bash patchelf --set-interpreter /wherever/ld-linux-x86-64.so.2 myexe
Keep in mind that glibc backwards compatibility means a newer glibc will work with code compiled with an older version, but not the other way around.
PaperWM is a scrolling window manager for the GNOME Shell. It's similar to tiling window managers like dwm and XMonad, but instead of tiling windows within the desktop, windows scroll to the right as they are created. So you just navigate left and right to various windows.
I like the conceptual idiom a lot better than normal tiling. I'm not sure if it works for me in practice. I've tried it in Debian 12/GNOME Shell 43.9 and installed the extension from extensions.gnome.org here on my laptop. I usually use each window in full screen on my laptop and alt-tab between them, so I'm not sure how useful the scrolling is for me. After an hour or so of use, it does seem to encourage me to have more than one window at a time on the desktop.
The default keybinding for switch window is super+tab, but I like alt+tab a lot better, so had to add that. The instructions on moving around and re-sizing windows are a bit confusing to me. I'm yet to really figure it-out.
Kakoune is a Vim-inspired editor. Major differences from Vim include a focus on multiple selections and "orthogonal design", meaning normal-mode keybindings are redesigned to be more orthogonal. It also follows the Unix philosophy by not having much built-in. For example, to format paragraphs into a certain number of columns, I select text, hit |
to pipe it to a shell program and run fmt
. I'd just run gq
in Vim.
I made this post with Kakoune. It took me a while to figure out how to get back to my text buffer from the documentation (type :buffer
and then it shows in the autocomplete). In Vim, I tend to select things in visual mode and then perform actions on them. So I do like the selection-verb idiom of Kakoune, but don't like that visual mode is missing. It would take a while to transition from Vim.
Borg seems like the best open source backup solution right now. It deduplicates chunks of data within files across multiple backups, saving a lot of space. Encryption and compression are also supported.
It only works locally or over SSH, and doesn't work on Windows. I wish it had support for parity so it could recover from a bit of storage corruption.
File copy/sync/move to and from the cloud, and even between clouds.
It works with S3 and similar, but also OneDrive, Dropbox, Google Drive, FTP, and SFTP.
Rsync for the cloud sounds useful, as well as copy and move. Two-way sync, bisync, sounds like a mess waiting to happen.
For early Israelites and Muslims, the pork taboo seems to be a way to identify with their ancestral origin as herders, where pigs weren't practical.
Beause pigs didn't need a bunch of graising land, government officials couldn't track them. They could live their entire lives in a backyard. This, combined with nice secondary products of ruminants like milk and wool, lead to pigs being a lower-class thing, while ruminants were prized by the wealthy and religious/social elite.
Dozens of images aren't required for a favicon. The article recommends 6, but I would try just .ico at only 32x32 and a SVG.
Includes a description of the files required and why, as well as how to generate them with Inkscape and Gimp.
Peer to peer file sharing protocol. Ideal for sharing ssh keys and similar. Very well regarded cryptography.
Funny musings about how systems programmers are hard-core.
Excerpts:
The systems programmer has traced a network problem across eight machines, three time zones, and a brief diversion into Amish country, where the problem was transmitted in the front left hoof of a mule named Deliverance
A systems programmer will know what to do when society breaks down, because the systems programmer already lives in a world without law.
Listen: I’m not saying that other kinds of computer people are useless. I believe (but cannot prove) that PHP developers have souls. I think it’s great that database people keep trying to improve select-from-where, even though the only queries that cannot be expressed using select-from-where are inappropriate limericks from “The Canterbury Tales.” In some way that I don’t yet understand, I’m glad that theorists are investigating the equivalence between five-dimensional Turing machines and Edward Scissorhands. In most situations, GUI designers should not be forced to fight each other with tridents and nets as I yell “THERE ARE NO MODAL DIALOGS IN SPARTA.” I am like the Statue of Liberty: I accept everyone, even the wretched and the huddled and people who enjoy Haskell. But when things get tough, I need mission-critical people; I need a person who can wear night-vision goggles and descend from a helicopter on ropes and do classified things to protect my freedom while country music plays in the background. A systems person can do that. I can realistically give a kernel hacker a nickname like “Diamondback” or “Zeus Hammer.” In contrast, no one has ever said, “These semi- transparent icons are really semi-transparent! IS THIS THE WORK OF ZEUS HAMMER?”
When you debug a distributed system or an OS kernel, you do it Texas-style. You gather some mean, stoic people, people who have seen things die, and you get some primitive tools, like a compass and a rucksack and a stick that’s pointed on one end, and you walk into the wilderness and you look for trouble, possibly while using chewing tobacco. As a systems hacker, you must be prepared to do savage things, unspeakable things, to kill runaway threads with your bare hands, to write directly to network ports using telnet and an old copy of an RFC that you found in the Vatican.
Nothing ruins a Friday at 5 P.M. faster than taking one last pass through the log file and discovering a word-aligned buffer address, but a buffer length of NUMBER OF ELECTRONS IN THE UNIVERSE.
You might ask, “Why would someone write code in a grotesque language that exposes raw memory addresses? Why not use a modern language with garbage collection and functional programming and free massages after lunch?” Here’s the answer: Pointers are real. They’re what the hardware understands. Somebody has to deal with them. You can’t just place a LISP book on top of an x86 chip and hope that the hardware learns about lambda calculus by osmosis.
That being said, if you find yourself drinking a martini and writing programs in garbage-collected, object-oriented Esperanto, be aware that the only reason that the Esperanto runtime works is because there are systems people who have exchanged any hope of losing their virginity for the exciting opportunity to think about hex numbers and their relationships with the operating system, the hardware, and ancient blood rituals that Bjarne Stroustrup performed at Stonehenge.
The best way to run javascript is in the head with the "defer" attribute. That immediately starts fetching the script, but waits to execute it until the whole page is parsed.
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="UTF-8">
<script src="blahblah.js" defer></script>
</head>
<body>
<p>Stuff here</p>
</body>
</html>
Interesting alternative to GitHub. It's also meant to be much lighter weight than GitLab, which I understand is quite heavy.
The hosted version is https://codeberg.org
Interesting architecture for a database specialized for OLTP. Single threaded, and each record is just 128 bytes to fit in a CPU cache line!
Cheet sheets on all kinds of security, from C-based toolchain hardening to HTTP headers.
A really nice guide to the best, most secure ways to do website authentication. Lots of nice links