More from Evan Hahn (dot com)
In short: Vim has a joke autocmd called UserGettingBored that doesn’t do anything. Vim’s automatic commands feature, usually shortened to “autocmd”, lets you run code when various events occur. For example, you could implement an auto-save feature by binding the TextChanged event to the :w command. Vim has over 100 events, from “buffer was created” to “file was saved”. But one of them sticks out to me: UserGettingBored. Here’s the documentation: UserGettingBored: When the user presses the same key 42 times. Just kidding! :-) When I saw this, I was busy doing something else and it completely derailed me. “I must know more,” I thought. Here’s what I found: Unfortunately, it doesn’t do anything. It only exists in the documentation (and some tests). If you try to use it with somethig like autocmd UserGettingBored ..., you’ll get a “no such group or event” error. It’s present in Vim, Neovim, and Vim Classic. It was first added by Bram Moolenaar in July 2000, over a year before Vim 6.0 was released. The original description was, “When the user hits CTRL-C. Just kidding!” And it didn’t do anything back then, so I don’t think it’s ever been real. In August 2001, he added the smiley face to the documentation. It then read, “When the user hits CTRL-C. Just kidding! :-)” Twelve years later, in 2013, the description changed to its current iteration: “When the user presses the same key 42 times. Just kidding! :-)” In 2022, developer Mike Smith created an unofficial plugin inspired by this joke autocmd. If you press the same key 42 times in Insert mode, a picture of Samuel L. Jackson appears. 22 years later, it’s finally real.
In short: I prefer strict tables in SQLite because they avoid some datatype problems, such as putting text in number columns. SQLite has a feature that I think is underrated: strict tables. Strict tables help enforce rigid typing, preventing mistakes like putting text into integer columns. I like them, and wrote this post to promote their use! To make a strict table, add STRICT to the end of its definition. Like this: -CREATE TABLE people (name TEXT); +CREATE TABLE people (name TEXT) STRICT; That’s it! But what does it do? Advantages of strict tables Broadly, strict tables help enforce rigid types, like other SQL engines do. Prevents type mismatches on insert/update Most significantly, strict tables keep you from inserting the wrong type into a column. For example, SQLite normally lets you put text into an INTEGER column, but not with strict tables. -- Non-strict tables let you put anything anywhere. CREATE TABLE people_nonstrict (age INTEGER); INSERT INTO people_nonstrict (age) VALUES ('garbage'); -- => works fine -- Strict tables don't allow that, which I prefer. CREATE TABLE people_strict (age INTEGER) STRICT; INSERT INTO people_strict (age) VALUES ('garbage'); -- => error: cannot store TEXT value in INTEGER column Personally, I think it’s a mistake to try to put text in an integer column, or vice-versa. I don’t want SQLite to let me make this error! The same validation happens for UPDATEs, too. Notably, if a value can be losslessly converted, it will still be accepted. For example, the string '123' can be perfectly converted to an integer, so it’s allowed. These two lines are equivalent, even for a strict table: INSERT INTO people_strict (age) VALUES ('123'); INSERT INTO people_strict (age) VALUES (123); Prevents bogus column types on table creation By default, you can create columns with bogus types. For example, all of these work even though they aren’t valid SQLite datatypes: -- SQLite doesn't support these types, but this is all accepted. CREATE TABLE tbl (name GARBAGE); CREATE TABLE tbl (name DATETIME); CREATE TABLE tbl (name JSON); CREATE TABLE tbl (name UUID); CREATE TABLE tbl (name BLOBB); I think these aren’t what the developer intended. Some of these are typos, some of them are misunderstandings of which datatypes SQLite supports, and some are egregious mistakes. Appending STRICT to any of these statements makes them error. In my opinion, that’s the correct behavior! -- All of these give errors, which I prefer. CREATE TABLE tbl (name GARBAGE) STRICT; CREATE TABLE tbl (name DATETIME) STRICT; CREATE TABLE tbl (name JSON) STRICT; CREATE TABLE tbl (name UUID) STRICT; CREATE TABLE tbl (name BLOBB) STRICT; Only INT, INTEGER, REAL, TEXT, BLOB, and ANY are allowed. Strict tables also require a column type, so you can’t do CREATE TABLE tbl (name). Still allows flexibility with ANY If you still need a column to be flexible, you can use the ANY datatype. As the name suggests, it allows anything—even in a strict table. CREATE TABLE tbl (value ANY) STRICT; -- All of these are valid because the column is ANY: INSERT INTO tbl (value) VALUES (123); INSERT INTO tbl (value) VALUES ('text'); INSERT INTO tbl (value) VALUES (12.34); INSERT INTO tbl (value) VALUES (X'8647'); I haven’t found a use for this, but maybe you will! Disadvantages of strict tables I prefer strict tables but I must share a few cons. Not everything is better! Can’t strict-ify an existing table I think it’s best to use strictness from the start, but that’s not always possible. Unfortunately, I don’t think there’s a way to ALTER a table to make it strict. I think you have to copy the data out of the non-strict table into the strict one. Something like this: -- 1. Create a new strict table with the same schema CREATE TABLE new_people (name TEXT) STRICT; -- 2. Copy data (risky if types are wrong!) INSERT INTO new_people SELECT * FROM people; -- 3. Replace the old table DROP TABLE people; ALTER TABLE new_people RENAME TO people; Note that this could be tricky if the non-strict table has invalid data! For example, if the old data accidentally contains text in an integer column, you’ll get errors when doing the migration. You’ll probably need to clean the data or cast it. You could make a rule for your codebase that all new tables are strict. That might be useful—at least some of your tables are valid! But it might also mean you have inconsistent validation across your tables, which might be more surprising than having weak validation on all tables. It’s up to you to decide whether this is a good fit for you. The SQLite developers disagree with me SQLite has a whole page called “The Advantages Of Flexible Typing”, where they argue that SQLite’s flexible behavior is good, actually. I hesitate to wade into the controversy of static-versus-dynamic, but I disagree in most cases. I’ve personally encountered many bugs where an unexpected data type caused subtle headaches. I’d much rather these mistakes explode loudly. But it’s worth noting that SQLite’s developers seem not to share my preference for strict tables! They point out a few good uses for flexible tables, such as “a pure key-value store” or “a place to store miscellaneous attributes” of different types. They also mention that you might want to keep the invalid data in some cases, like if you’re directly importing a messy CSV and don’t want to lose any data. I still prefer strict tables, but acknowledge there are some reasonable cases for non-strict ones. (There’s also at least one comment in the SQLite source that calls non-strict tables “legacy”, but I trust that less than the official documentation.) Only in SQLite 3.37.0+ SQLite introduced strict tables in version 3.37.0, released November 2021. If you’re on an older version of SQLite, you can’t use strict tables. It’s worth noting that old versions of SQLite can’t read databases with strict tables. For example, if you create a strict table in the newest version of SQLite and then try to read that database in SQLite 3.36.0 (before strict tables were added), you’ll get an error—even if the strict table is already in the database. Performance maybe? Strict tables are theoretically slower because they have to do a little extra work. For example, they check datatypes when doing an insert or update. But in practice, I don’t think this is an issue. I wrote a hacky script that inserted millions of rows into a table with 100 columns, and there was no obvious difference on multiple machines I tried. The file size on disk was also the same. I didn’t test this thoroughly, so maybe there’s something I missed, but I don’t think strict tables present a performance problem. In fact, one might expect better performance because you won’t be accidentally mismatching SQLite’s column affinities. But again, I haven’t tested this. Conclusion: I like strict tables! Personally, I think the pros of strict tables outweigh the cons. I generally prefer when types are rigidly enforced. It squashes a class of mistakes, and help enforce good data integrity. They’re not a panacea, but they’re usually easy to add and go a long way. If there’s a SQLite feature you think is underrated, please tell me.
As I write this, we’re about 7 sixteenths through 2026, and it’s about 14 sixteenths through the day. For the sixteenth issue of the Taper online magazine, I split time into sixteenths to think about its passage in a different way. The code, which had to be under 2048 bytes, isn’t terribly complex. It does some date math and uses a Go server for minification. If you want, here’s the unminified source code. Go check out all the other entries from this issue! My favorites include "[SIC]", “Desperate Measures from a Dying Regime”, and "(un)done". See also: my previous Taper entry.
I wrote a simple script that translates text at the command line, completely offline. Here’s an example of how it works on my computer: echo '¿Cómo estás?' | translate # => How are you? It combines a few tools: TranslateGemma, a special-purpose language model for translation Ollama, a tool for running language models locally Efficient Language Detector, a library that detects the language for a piece of text Here’s the pseudocode of how it works: source = read_stdin() # Uses Efficient Language Detector source_language = detect_language(source) # Uses JavaScript's `navigator.language` target_language = get_system_language() # Uses Ollama + TranslateGemma return translate(source, source_language, target_language) I built this because I couldn’t find anyone else who had done it. It’s written in Deno for my specific needs—for example, it only translates text into your system’s language—but could easily be adapted if you need something else. I like that I can do offline, private, automatic translation. It’s imperfect, but useful for me! Here’s the source code.
More in technology
Associate Professor Cathy Wu uses reinforcement learning to help map out improvements to transportation and other multifaceted systems.
After living with Apple’s 27.0 OSs since launch, I have some more annoyances to get off my chest. This time, it’s all about how tabs and menus have gotten worse. I’ve already ranted about the Liquid Glass material in general, but these two design changes in particular have really been grinding my gears. I’ll reiterate that Apple’s latest OSs look substantially nicer to me than the previous set… but that only makes these setbacks more glaring. Also, many of these issues aren’t nearly as bad in light mode — but I use dark mode exclusively on all platforms. Apple offers this appearance setting, so I think it’s fair to criticize them when it’s not holding up. First up, let’s talk tab bars. I think these looked awful in the original Liquid Glass redesign, and in 27.0 they look even worse. Below is an example of three tab bars from Safari in macOS. All of them are in dark mode. The top example is from macOS 26 with the “clear” Liquid Glass setting, the middle is macOS 27 with the default (mid-slider) version of Liquid Glass, and the bottom is macOS 27 with Liquid Glass at its most tinted. In each, the middle tab is selected (though I think the word “tab” is being quite generous to these globs). In macOS 26, there was practically no difference between the clear and tinted versions of tabs. Similarly, the clearest and default/middle tabs in macOS 27 are effectively the same. Because of this, I’m leaving out the redundant examples. Even though I still think it’s ugly, I vastly prefer the macOS 26 version of these three options. It offers the most contrast, and it makes more sense in dark mode: the background is darker and the foreground of the active tab is lighter. The middle example is what tabs look like in the default (mid-slider) version of Liquid Glass in macOS 27. There’s now only a very faint outline around the active tab, and practically no difference in background colours. To me, this is unreasonably subtle. The effect is even worse when there are a lot of tabs open. Lastly, there’s macOS 27 with the fully tinted Liquid Glass setting. It’s better, but it still looks less correct to me than the tab design from macOS 26. It’s difficult to put into words how much I loathe the look of this new tab bar design. I don’t mind the more “bubbly” look of Liquid Glass throughout the 27 OSs for the most part. It gives UI elements more dimension than in the 26 OSs. But it doesn’t work for tabs. Because the bubbly look is inside a trough, the active tab’s glass effect ends up looking like a blur on the top and bottom. This reduces contrast further and makes the active tab harder for me to pick out. Even in dark mode with full tint, I find the active tab less visually clear than in 26’s clear mode. Now, I’m sure there are at least a few people reading who don’t see what the fuss is about. If that’s you, I assure you that the difference is more stark when you’re not comparing things side by side. It’s not impossible for me to pick out the active tab, but I think it’s trickier than it needs to be! But, if you still don’t believe me, here’s a little experiment. Which of these do you think is most legible? The text/background colours in the above image are based on the foreground/background colours used in the tab bar instances above. First is clear in macOS 26, then the default from macOS 27, then fully tinted in macOS 27. I think they’re all pretty awful, but I prefer the macOS 26 clear version. Again, this is because I’m using dark mode. In dark mode, light text appears on a darker background. Similarly, active UI elements have a lighter background than their surrounding elements. I’m sure there are counter-examples, but this is how just about everything else works in Apple’s own apps! It should be noted that Safari uses the system default tab bar design. I also see this design in Apple’s Terminal app, in Pixelmator Pro, and elsewhere. I don’t use Xcode daily anymore, but you’ll also find them there — though in true Xcode fashion, they’re ever so slightly nonstandard and also don’t respect your tint setting. Below is a screenshot of Xcode using my current settings of dark mode with fully tinted Liquid Glass. Up next: menus. Below is an image showing four versions of the same menu in macOS. Top left is macOS 26 clear, top right is macOS 26 tinted, bottom left is macOS 27 default (mid-slider), and bottom right is macOS 27 fully tinted. It’s a similar story here. In macOS 26’s dark mode, I had no problem with system menus even when Liquid Glass was set to clear. In macOS 27, even in the fully tinted mode, the menus have much lower contrast. They also now lose all of Liquid Glass’s refractive effects when at their most tinted. I think this is less of an issue than the tab design changes, but it’s still a downgrade. Again, I’m certain many people don’t care about this. Some might wonder why I’m not turning on accessibility settings to help with these things, if they bother me so much. I’ve flirted with this (especially the “Increase Contrast” setting), but those settings have many knock-on effects. 1 But honestly, I don’t think accessibility settings should be required to have a reasonable amount of contrast in a design system. Maybe Apple disagrees, but I really hope more dark mode tweaks are coming. The “Increase Contrast” setting is under System Settings -> Accessibility -> Display -> Increase Contrast. Interestingly, you can use this setting along with the clearest version of Liquid Glass to almost get back to how things looked in macOS 26’s version of tinted. However, it adds contrast-y lines around many UI elements that I find extremely distracting. It also alters colours on some elements to, strangely, make them less contrast-y. It feels unevenly applied and poorly implemented in several apps. ↩
Trump wants to add a citizenship question and ban questions on race
I recently watched the talk by Scott Jenson titled "Are we really going to use the same Desktop UX forever?" https://www.youtube.com/watch?v=V7AfAcQwLW0&t=445s. He's a great presenter, really articulate and concise. The kind of speaker that you'd