As someone who lives in a terminal simulator, I’m pleasantly surprised by the new toys we get in recent years such as fzf, ripgrep, fd, etc. A great number of these are written in relatively young programming languages such as Go and Rust. But, noticibly, none of them are written in Swift.
In this post, I’ll try to explain why that is.
Unix-like virtual file systems has been around for decades. API that manupulates
such systems has standardized a long time ago and exists in most computers
running Linux/BSD/macOS today (and, to a large extend, smart phones). To Swift
users, Using these APIs is straight-forward (
So Swift programmers are all happy campers (re-)inventeing all sorts of file system utilities, right?
Well, not quite.
Okay, I lied about POSIX APIs being “straight-forward” in Swift. Or rather, this is very subjective.
Continuing with the
rmdir example, we must first import it from either
Darwin, depending on your OS. To know whether the operation is successful,
we need to see whether it returned integer 0. To learn why 0 was not returned,
we need to read the “magical” variable
errno could be written to by
other APIs so we’d better capture it in time…
And that’s one of the simpler APIs in POSIX calls!
Programmers whine about ergonomics partially because we are previlidged and spoiled. But mostly because our attention is a limited resources. Mixing API conventions distracts us from solving the problem at hand. Bad ergonomics, therefore, drives away a good potion of users who cares about quality of their tools.
As of this writing, the release of Swift 5 is imminent. The vast majority of existing Swift code is written to run on iOS. The concept of a file, or the traditional virtal file system, is hidden to iOS users, and sandboxed for developers. I bet most Swift users rarely think about the fact that there’s a entire set of POSIX API at their disposal.
Foundation alleviates the need to deal with files and directories:
locates the files;
UserDefaults or the keychain is your primary
way to persist data;
NSCoding has methods to read and
write to files. And finally, if you really need to deal with files,
NSFileManager has everything you’ll ever need.
Why would a productive Swift programmer think about POSIX in this environment? Why would a tutor teach POSIX over the useful/practical/”native” alternatives?
We can trace “riding on the Apple platform” mentality back to the pre-iPhone days, where a very small Mac developer community labors on on a niche platform (compared to iOS today) and they loved it. However, I’m sure they used more POSIX stuff back then than the average iOS developers today.
Having a great library such as Foundation on the most popular developer platform where the language thrives means it’ll take longer for “subcultures” to emerge, if they do at all.
File system APIs being in
Foundation as opposed to the standard library is
probably a temporary condition. Nevertheless, it has at least the following
Its quality of implementation is not held on the same standard that those
APIs in the standard library. This is especially true for the separate,
Foundation implementation. Getting consistent and correct
behaviors across macOS and Linux is hard.
A person learning Swift won’t explore the language with a file system API. This I suspect, is the most important reason many of these great CLI utilites are written in other programming languages. Programmers seek instant gratification when they learn. And they usually stay in a limited domain (like iOS) at first. This is where the built-in library is special: no matter which domain is chosen, it’s always available. Languages such as Go and Rust include things like paths and files in their built-in library. Playing with these APIs while learning the lanugage plants a seed for future, serious, projects. There are less users of these languages compared to Swift, but there are more people thinking about projects that involves file systems in thoes communities. (Note I don’t have statistics here, just a guess.)
The next killer CLI tool is still more likely to be written in Go or Rust, than in Swift. Hopefully, somewhere in these speculations is a true cause of this phenomena. Maybe someone reading this will be inspired to accelerate change that will eventually revert the condition. (I’m trying).