Evan Hahn's blog

https://evanhahn.com/blog/

My blog, mostly about programming.

フィード

記事のアイキャッチ画像
Experiment: making TypeScript immutable-by-default
Evan Hahn's blog
I like programming languages where variables are immutable by default. For example, in Rust, let declares an immutable variable and let mut declares a mutable one. I’ve long wanted this in other languages, like TypeScript, which is mutable by default—the opposite of what I want!I wondered: is it possible to make TypeScript values immutable by default?My goal was to do this purely with TypeScript, without changing TypeScript itself. That meant no lint rules or other tools. I chose this because I wanted this solution to be as “pure” as possible…and it also sounded more fun.I spent an evening trying to do this. I failed but made progress! I made arrays and Records immutable by default, but I couldn’t get it working for regular objects. If you figure out how to do this completely, please contact me—I must know!Step 1: obliterate the built-in librariesTypeScript has built-in type definitions for JavaScript APIs like Array and Date and String. If you’ve ever changed the target or lib options
3日前
記事のアイキャッチ画像
Fizz Buzz without conditionals or booleans
はてなブックマークアイコン 1
Evan Hahn's blog
I recently learned about the Feeling of Computing podcast and listened to the latest episode. One of the hosts challenged listeners to “write Fizz Buzz with no booleans, no conditionals, no pattern matching, or other things that are like disguised booleans.”Here’s my Python solution:from itertools import cycledef string_mask(a, b): return b + a[len(b) :]def main(): fizz = cycle(["", "", "Fizz"]) buzz = cycle(["", "", "", "", "Buzz"]) numbers = range(1, 101) for f, b, n in zip(fizz, buzz, numbers): print(string_mask(str(n), f + b))main()This solution is basically three things put together:Create endless cycling sequences of "", "", "fizz", "", "", "fizz", "", "", "fizz", ... and the same idea for buzz.Combine those two sequences with the numbers 1 through 100 using zip, to get the following sequence:...("", "", 7)("", "", 8)("Fizz", "", 9)("", "Buzz", 10)("", "", 11)("Fizz", "", 12)("", "", 13)("", "", 14)("Fizz", "Buzz", 15)("", "", 16)...Convert the number to a string, then “mask” it
9日前
記事のアイキャッチ画像
"Understanding Unicode": my October 2025 talk at Longhorn PHP
Evan Hahn's blog
I gave a talk, “Understanding Unicode”, at Longhorn PHP last month. It’s loosely based on “Why does ‘👩🏾‍🌾’ have a length of 7 in JavaScript?”, a blog post I wrote in 2023.Though the talk is for PHP developers, I think it’s applicable to anyone who wants to understand more about Unicode, UTF-8, and more.Here’s the recording:Video You can also download the slides here.If you want to learn more about this topic through a PHP lens, see this more detailed presentation by Andreas Heigl.
11日前
記事のアイキャッチ画像
Kirby Air Riders demo impressions (from a big fan of the original)
Evan Hahn's blog
Kirby Air Ride patch art by @threadbreak_embroidery I like to think I’m a pretty big fan of Kirby Air Ride. I’ve played the game for thousands of hours since its release in 2003. I have a TV in my house hooked up solely to play Kirby Air Ride, which I play at least once a week. I’ve always felt this game was underrated. I yearned for a sequel, but had given up hope after two decades.In April, out of nowhere, Kirby Air Riders was announced. I was floored.This weekend, after what felt like 22 years of waiting, I finally got to try it this weekend. This was in the form of Kirby Air Riders: Global Test Ride, which served both as a network test for the developers and a sampler for players.I played it for hours. Here are my impressions.City Trial: more chaotic, still a roll of the diceThe original Kirby Air Ride presents as a racing game, but any fan will tell you the real treasure lies in the City Trial mode. City Trial drops all players in a shared space—the titular city—and has them freel
11日前
記事のアイキャッチ画像
Notes from "Tor: From the Dark Web to the Future of Privacy"
Evan Hahn's blog
I just finished Tor: From the Dark Web to the Future of Privacy, a profile of the Tor Project. I thought it was an approachable overview of Tor and its history. If you’re interested, the book is free! Or you can read my chapter-by-chapter notes below.Chapter 1: Privacy WorldsThe book claims that Tor “recreates the utopianism of the early internet pioneers, in which many users felt the connective power of the whole internet at their disposal. They experienced a global public sphere that was much harder for governments to control (even if with much of the poison that we see today).”It also talks about how “we write our values into the technologies we build” and how imported technologies “often bring their own cultures with them”. “So if we want to understand Tor and how it has shaped the landscape of online privacy across its history, we might try to pick apart the privacy values of its designers to understand how those values have shaped it over the years.”Chapter 3: Tor’s Strange Begin
13日前
記事のアイキャッチ画像
Notes from October 2025
Evan Hahn's blog
Wow, another month gone by. Here are my notes.Things I publishedI’m not a PHP developer but I was invited to speak at Longhorn PHP, where I gave a talk about understanding Unicode. I’ve given a version of this talk before but it was fun to adapt it for a PHP crowd. Hopefully a recording will be posted soon. (Update: you can now watch the recording of my talk, “Understanding Unicode”.)“Scripts I wrote that I use all the time” was a quickly-written summary of a bunch of scripts from my dotfiles. Little did I know that it would be my most popular post of all time (I think)! Lots of people reached out with their own useful scripts, it was highly upvoted on Lobsters and Hacker News, and it made it to a few newsletters. I should’ve known it would be popular because programmers love talking about dotfiles—even more than they like talking about AI.And, like every month, I wrote a few articles over at Zelda Dungeon.Greetings from fascismIt’s hard to pay attention to tech stuff when ICE is abduc
21日前
記事のアイキャッチ画像
Scripts I wrote that I use all the time
Evan Hahn's blog
In my decade-plus of maintaining my dotfiles, I’ve written a lot of little shell scripts. Here’s a big list of my personal favorites.Clipboardcopy and pasta are simple wrappers around system clipboard managers, like pbcopy on macOS and xclip on Linux. I use these all the time.# High level examplesrun_some_command | copypasta > file_from_my_clipboard.txt# Copy a file's contentscopy < file.txt# Open a file path from your clipboardvim "$(pasta)"# Decode some base64 from the clipboardpasta | base64 --decodepastas prints the current state of your clipboard to stdout, and then whenever the clipboard changes, it prints the new version. I use this once a week or so.# High level examplepastas > everything_i_copied.txt# Download every link I copy to my clipboardpastas | wget -i -cpwd copies the current directory to the clipboard. Basically pwd | copy. I often use this when I’m in a directory and I want use that directory in another terminal tab; I copy it in one tab and cd to it in another. I us
1ヶ月前
記事のアイキャッチ画像
Notes from September 2025
Evan Hahn's blog
Things I did and saw this September. See also: my notes from last month.Things I didI asked Ben Werdmuller for advice on “the best way for technologists to apply their skills to positive change”, and he gave a great answer. (I didn’t really do much here…all I did was ask the question.)“People read your blog in many different ways” was an attempt to capture the huge number of different types of readers you might have. I don’t know if this one is useful, but this kind of thinking is helpful for me.Following NetBSD, QEMU, and Gentoo, I updated Helmet.js’s guidelines to discourage AI contributions.I’ve long disliked @ts-ignore in TypeScript, so I published "@ts-ignore is almost always the worst option".In my effort to fill in the internet’s missing pieces, I posted a bit about JavaScript fetch’s character encoding. Hopefully I’ve helped the next person with this question.And as usual, I wrote a few articles for Zelda Dungeon this month. I’m happy the ZD editors let me get a little deranged
2ヶ月前
記事のアイキャッチ画像
People read your blog in many different ways
Evan Hahn's blog
I read a lot of tech blog posts and have written a few of my own. I think a lot about the varied ways people read.How thoroughly do people read?Some readers read every word from start to finish. (I suspect this is rare.)Some readers skim.Some readers don’t read your post at all.How much of the language do they understand?Some readers read in a language they’re fluent in.Some readers read in a language they understand, but not fluently.Some readers read an automated translation because they don’t speak the language.Some readers have great vocabularies and understand when you use words like “synecdoche”.Some readers have smaller vocabularies and benefit from simpler words. (This is me.)What software are readers using to consume your post?Readers use varying screen sizes. Phones, laptops, tablets, maybe even refrigerators.Readers use varying font sizes. For example, I increase my display’s font size for readability.Some readers listen to your post with text-to-speech.Some readers use a “r
2ヶ月前
記事のアイキャッチ画像
@ts-ignore is almost always the worst option
Evan Hahn's blog
In short: in TypeScript, any and @ts-expect-error are almost always better than @ts-ignore.Sometimes, I want to ignore a TypeScript error without doing a proper fix. Maybe I’m prototyping and don’t need perfect type safety. Maybe TypeScript isn’t smart enough to understand a necessary workaround. Or maybe I’m unable to figure out a solution because I’m not a TypeScript expert!In these moments, I’m be tempted to reach for a // @ts-ignore comment, which will suppress all errors on the following line. For example, this code will report no errors even though the type of foo is wrong:// @ts-ignoreconst foo: string = 123;This quick fix is even recommended by editors like Visual Studio Code, and seems like a reasonable solution when I just want something done quickly. But, in my opinion, @ts-ignore is almost never the best choice.Alternative 1: @ts-expect-error@ts-ignore has a sibling: @ts-expect-error.@ts-ignore tells TypeScript to ignore the next line. @ts-expect-error asks TypeScript to ig
2ヶ月前