HNHacker News
TopNewBestAskShowJobs

cstrahan

1,132 karma · joined January 10, 2012

Charles Strahan cstrahan.com

Software Developer in Washington, DC

[ my public key: https://keybase.io/cstrahan; my proof: https://keybase.io/cstrahan/sigs/yV0wzDolQldJ9GSMuflLE9_PIggrsCf9jzIQPUHqzKE ]

submissionscomments
cstrahan··on Chronic fatigue syndrome may have a post-viral infection origin
That’s my guess. Got EBV back in 2015, and spent about a year with debilitating photophobia, visibly bulging lymph nodes, along with a host of symptoms that still haven’t gone away: excruciating joint pain, muscle weakness, headache and brain fog, anhedonia, annihilated sex drive, etc.

Trying to manage my symptoms, I got a hormone panel to compare pre-EBV. Testosterone and Estradiol dropped by about 65%, where it remained for 3 years until I convinced a doctor to take that delta seriously.

I’m better on TRT now, but I’d say I’m at about a solid 65% of my former self. Joint and muscle pain is gone, but I don’t have the same drive and still struggle with anhedonia. Would love to try a cocktail of things — almost anything really — to feel more alive, but I don’t want to do antidepressants, and I don’t want to be branded “drug seeking”, so I just try to get by for now.

cstrahan··on Mullvad on Tailscale: Privately browse the web
I could be wrong, but I would think that a conveniently timed power outage would prevent the memory dumping scenario :)
cstrahan··on Show HN: Nix Snapshotter – Native understanding of Nix packages for containerd
To answer that, I think you’d first need to define what “putting my Nix config in e.g. a Dockerfile” means.

Do you mean using a Dockerfile to run a Nix package build, embedding the resulting files (and transitive dependencies) in the image file? If so, this objectively superior in several ways: 1) only one copy of any package exists on disk: the one in the Nix store, instead of multiple images having independent copies. 2) Related to this de-duplication, it will be faster to go from pristine host (virtual machine, dev machine, whatever) to having containers up and running, as you no longer have bulky images to transfer over the network. 3) I haven’t looked at the finer details, but I’m pretty sure this means that the host can build the required packages once, meaning better caching — instead of multiple Dockerfiles rebuilding the same packages, the host builds once and all dependent containers get use the host’s Nix store, again without any copying of files involved.

Edit:

From hinshun’s comment above, point #3 is incorrect — either the host’s Nix store would need a copy already present, or the package would need to be available in a remote store/cache. Even with that correction, you still avoid redundant package builds — either the package is available remotely/locally, or the image fetch fails — so things are still only ever built once :)

cstrahan··on GNU Parallel, where have you been all my life?
I also recommend checking out `xe`: https://github.com/leahneukirchen/xe

It’s like xargs with sane defaults and a couple tricks of its own.

cstrahan··on Write Elixir NIFs in Rust
If you're asking if it's possible to use C (or any language that can export functions conforming to the C ABI), then yes: https://en.wikipedia.org/wiki/Java_Native_Interface
cstrahan··on Ask HN: Why do web sites not place the cursor for input?
An example from personal experience:

When I was really young, I wanted to figure out web programming. I knew C#, so when I came across a book on making websites in C# with ASP.NET, it was a no-brainer.

I the slogged through 2000+ pages on how to handle connection state, WYSIWYG web forms generation with drag and drop component, etc. The experience was horrible, and turned me off to web programming.

A brief aside, for those unfamiliar: ASP.NET presents the programmer with a (leaky) abstraction over HTTP and HTML, pretending that the protocols involved are stateful, whereas the reality is that HTTP is stateless. This creates all sorts of new ASP.NET specific problems, which are then addressed by jumping through hoops to ensure control state is (de)serialized just right, etc. Thus the 2000+ page count.

Later on I discovered ASP.NET MVC, which did away the the “let’s pretend HTTP is stateful”, and takes an approach much like just about any other web framework out there. You’re expected to know HTML, CSS and HTTP. I was like “oh, is this it? I just learn some HTML, register some request handlers, and generate some HTML?”. Until then, my focus was on getting something working, and I trusted that what they covered would get me up and running. That line of reasoning failed me with ASP.NET, as the book set me up to ignore the underlying (and ironically, much simpler) machinery of the web.

I’m positive ASP.NET isn’t the only framework guilty of distracting developers from learning the fundamentals. And think about it: when’s the last time you’ve seen HTTP or HTML listed as required proficiencies in a job description? I can’t remember. What I do see is an expectation to know all the intricacies of React, Babel, Grunt, Redux, whatever. If you’re trying to land your first job, or working on climbing the ladder, it’s easy to fall into the trap of investing all your time in finding your way around the accidental complexity of the frameworks you intend to make your living from using, and underestimate the value of understanding what’s going on in the layers beneath the abstractions.

cstrahan··on Stay or Leave: Manager Admitted I am on PIP because quota
Suppose his coworkers are all working unreasonably long hours (whether out of fear, or poor work-life balance, or whatever), and he’s “only” working 40-45 hours a week. His output could be excellent, but he’d still be on the chopping block, no?

I don’t think him being chosen to receive the boot necessarily means his performance was poor. I mean, maybe it was, but from the outside I wouldn’t jump to that conclusion.

cstrahan··on Local async executors and why they should be the default
Any goroutine can be interrupted to let another goroutine run on the respective OS thread, without the involvement of the code author. The thing managing this scheduling behind the scenes is the runtime. Also, the entire standard library is built atop non-blocking IO, which again is managed by the runtime behind the scenes (e.g. waiting for file descriptor readiness, scheduling the respectively blocked goroutine, etc). And there is no “async system call wrapper” involved - these are fundamentally different system calls (for epoll or kqueue or whatever).

You could argue that all go code is async (in the Rust sense), and because of this uniformity, there’s no need for the syntactic distinction that Rust requires you to make.

Rust could have done the same (and at one point in time they did have green threads), Rust decided that the convenience to Rust programmers wasn’t worth the cost elsewhere (the inclusion of a runtime, performance overhead, C interop, etc).

cstrahan··on Using XPath in 2023
That made me chuckle.

For those not familiar with the promise design controversy:

http://brianmckenna.org/blog/category_theory_promisesaplus

https://github.com/promises-aplus/constructor-spec/issues/24

https://github.com/promises-aplus/promises-spec/issues/94

cstrahan··on Penrose 3.0
FWIW as a non-mathematician: when I hear “Penrose” I think “Penrose Diagram”. The name association would benefit me as a sort of mnemonic: if I saw a binary called penrose on my system, I would guess it has something to do with diagrams (not necessarily Penrose diagrams). Similarly, if I forgot the name of this tool, it would be easy to recall, as there aren’t too many “____ diagram” word pairs floating around in my brain. I give libraries and executables pithy names along the same lines, where one word that isn’t already in use (“Penrose”) strongly associates with another more general word (“diagram”) in my memory.

Look at the VHS tool for example — it doesn’t have anything to do with physical VHS tapes, but it does record a scripted shell invocation as a GIF for embedding in docs and demos and such. Super easy name to remember. See https://github.com/charmbracelet/vhs

cstrahan··on We put a distributed database in the browser and made a game of it
"Simulation" doesn't necessarily entail stimulating interactivity and a pleasing aesthetic. As of writing, I don't know if there's a better term than "game" for what this is. (Maybe we can coin a new term, like "game-lite" -- somewhat akin to what "rogue-lite" is to the "rogue" genre).

See "walking simulators":

https://en.wikipedia.org/wiki/What_Remains_of_Edith_Finch

https://en.wikipedia.org/wiki/The_Stanley_Parable

https://en.wikipedia.org/wiki/Thirty_Flights_of_Loving

Plenty argue that these aren't games either (usual complaints involve lack of problem(s) to resolve, and no win-lose dynamic); but, then, what are these? The closest category I can think of would be "computer-animated film", but... these are interactive, and you can navigate and look in any direction you want, which yields a very different experience than watching a film like "Toy Story".

cstrahan··on Windows NT on 600MHz machine opens apps instantly. What happened?
> What makes it even worse is how unpredictable the lags are so you can't even train yourself around it.

What is worse than that: not being able to predict if inputs will be buffered or dropped during unresponsiveness. I kind of look like an idiot when I keep clicking/typing away on someone else’s computer while things are frozen, thinking “oh, it’ll catch up in a bit”, and then 5 seconds later I have to work harder to fix the chaos: some keystrokes at the beginning made it through, then only every other one, then the next couple hundred got dropped, but the next 100 came through fine, and interspersed everywhere there are bizarre runs of duplicated keys, as if I had held the letter aaaaaaaaaaaaaa down continuously.

Everyone’s mix of hardware, OS, text editor, text editor plugins, etc makes this behavior highly variable, and hard to guess if it makes sense to keep typing or just wait out the frequent 1-5 second lockups.

cstrahan··on OpenBSD: Shutdown/reboot now require membership of group _shutdown
Or this:

OpenBSD: membership of _shutdown group now sufficient for shutdown/reboot.

Requirement and sufficiency are very different things.

cstrahan··on My First Impressions of Nix
And as a pointed amendment here, here's a non-Nix experience I've had, which everyone eventually has:

My team discovers a major issue in a recent release. Conceptually, we need to rollback and we need to do so fast. However, we can't just deploy the same package/docker-image/whatever from the last release (perhaps because of a change in database schema, or whatever) -- we need the previous version plus some minor patches.

So I check out that previous version from source control.

I do `docker build`, and see a screen full of errors.

Huh. That worked when this ran in CI a week or two ago.

Oh, due to some petty political issues the devs of some third-party library (that is deep within our tree of transitive dependencies) have decided to nuke every posted version to pypi/rubygems/whatever and deleted the repo from GitHub.

Yaaaay. Okay. Fuck.

Would have been nice if we had a local cache/mirror of that, but there's nothing about docker that guarantees you'll have a copy like that (Nix does provide this).

Gotta push through the panic and weight options:

Do we try to replace every dependency that uses this lib?

Holy smokes that (transitively) touches a ton of our code base. Not impossible to do, but almost impossible to consider what the ETA will be for that, and remember that we're trying to put out an active fire here.

So... we could see if anyone online has a fork of the repo...

But then how do we know it hasn't been tampered with? It's not like pip/npm/rubygems has some content-based hash we can use to assert that this library's source tree is as expected, and it's not like Docker tracks/asserts such a content-based hash (Nix does track this).

Cue an hour or two of immense stress and customers being very unhappy. Oh, and there's nothing to say that this won't happen again -- after all, docker does nothing to help with and/or enforce determinism/reproducibility of builds. I mean, it does guarantee runtime reproducibility in that multiple machines can be running precisely the same image, but docker does not guarantee that you can reproduce the build of a given Dockerfile. These two different types of reproducibility should not be conflated.

Nix is specifically designed to prevent these types of scenarios.

cstrahan··on My First Impressions of Nix
I suspect you may take other technology you use as granted, and have forgotten the inherent subtlety that you've had to work through to attain the working understanding you have today. Consider the confusion you might have being an aspiring developer, on your first day cracking open a book on $INSERT_LANG_HERE, and then someone mentions Docker. Imagine your confusion:

"What is docker? It builds things? So, like, a "build system"? But it invokes something called make, or cmake, or... well, aren't those build systems? If that isn't confusing, I don't know what is. Oh, you say it's software that runs on Linux -- what's that? So docker is an OS? No? It runs on an OS? And you're telling me it doesn't just build stuff, it also can coordinate the execution of stuff? Oh, you're telling me that's docker-compose -- that isn't docker itself? I mean, it had 'docker' in the name. Oh. So separate project to 'docker the container image making thingy', but can be used with that 'docker'. Good God this is confusing."

Much like the Docker and its ecosystem as whole -- along with all the enabling/leveraged tech underlying it, like chroots, user/pid/network/etc namespaces, union filesystems, seccomp, etc -- there is some inherent complexity in Nix (and its broader ecosystem of tools).

Just like docker has docker, docker-compose, docker images, stateful docker containers (is it running? dead? what subtree of my filesystem is mounted where in the container? etc), a whole syntax for "docker files", etc, we have multiple things that come together to make "the whole of Nix" what it is.

As a whole, for Nix we have (non-exhaustive, but hopefully broad enough to help paint a picture):

- Nix the language (analogous to Dockerfile syntax), which is used by

- Nix the package builder/manager (analogous to the `docker` binary)

- Nixpkgs, which is a collection of packages (a bit of a stretch if you take it too literally, but analogous to a collection of Dockerfiles, each for a different software)

- NixOS, an operating system that leverages the packages declared in Nixpkgs.

That hopefully explains the "WHAT", but says nothing of "WHY".

So to touch on the "WHY" a bit:

- Dockerfiles are not reproducible. What builds today may fail tomorrow, or next month, or on odd numbered days, or whatever.

- Nix packages are reproducible. If it builds today, it will build on any machine, any time. Guaranteed.

- On distributions like Debian/Ubuntu/etc (essentially anything that isn't NixOS or inspired thereby, like Guix), when you install a package, and something goes wrong, your system can end up in an in-determinant state that requires human intervention to manually sort out how to unbreak things. Just google (or just recall the last time it's happened to you -- and if it hasn't happened to you, it eventually will) people desperately asking for help to unbreak their system after, say, Ubuntu's apt (or whatever) gets screwy and says it can't install/uninstall anything else because $INSERT_CRYPTIC_APT_BUZZWORDS_HERE. Oh, and add to that the compounding effects of the fact that any package can include raw bash commands as post-(un)install steps -- that's just asking for packages to fail to (un)install cleanly depending on what else the user does/doesn't have installed at that point in time.

- On NixOS (and similar) the system state is guaranteed to either update fully or not at all -- as a consequence of its design, none of the chaos mentioned can happen. It's not that it's unlikely. I literally mean it simply can't happen. System updates are guaranteed all or nothing. Also, if you find out that some configuration change isn't what you wanted, you can roll back in instant (that's not hyperbole -- rolling back entails no slow error prone file copying/writing/etc, literally just one symlink change, and "poof" you're on the previous version).

In a nutshell, from top to bottom of the stack, the selling point is: imagine a world where "oops -- that failed in production, but... well, it worked on my machine, I promise!" simply can't happen. Imagine the time savings, and the reduction in anxiety if you knew that, if you could successfully build and/or deploy a particular piece of software once locally/in-staging/etc, you have full confidence that you can repeat that on any machine at any time.

Admittedly, if you enjoy those struggles with conventional non-Nix(OS) setups, there isn't much value to Nix.

cstrahan··on Consciousness and Anaesthesia (2009)
> As for free will, anyone saying we don’t have it is doing bad science since every person knows they do.

Free will: the power of acting without the constraint of necessity or fate; the ability to act at one's own discretion.

A known side effect of Parkinson’s drugs is the development of compulsive, (hypo)manic behaviors like gambling and hypersexuality — people who have been very straight laced their entire lives can change drastically. Are you suggesting that we should hold such patients equally accountable for making poor choices, as they are just as free willed as anyone else?

What of people with crippling, general anxiety, of no fault of their own, but simply as a result of genetics or exposure? If they aren’t as productive or let things slip, is that because they simply aren’t trying hard enough? (Even there — if we say one is trying harder, does that not imply an impediment to “act at one's own discretion”?)

Perhaps we should consider banning ADHD medication altogether — given everyone has free will, what practical difference does it make to put a substance into people’s bodies — they can just choose to make good decisions, right?

Of the decisions you regret, and knew that you would before acting, why did you make them? Why haven’t you just made the meta-choice of choosing to choose the right things from here on out? You can see the utility in making such a choice, right? So can you make that choice? Your discretion would likely be to make that choice, so all that’s left is to act on that discretion, which should be no problem because you have free will and you are not constrained by the natural chemical processes occurring in your brain and the physical laws that define them.

cstrahan··on Sandstorm: Open-source platform for self-hosting web apps
> But isn't this just what we used to call "software" ??

By way of analogy:

You could carry around a phone that has a simple Linux distro running on it. And while we're at it, what's the point of having a package repository? So we could get rid of that, and compile all our software from source. So the user experience is something like: SSH into your phone, curl down a source tarball for the calculator software you would like to run on your phone, and first make sure you have all of the build dependencies installed, and now make && sudo make install.

So what's the point of Android or iPhones?

Well, I'm very technically proficient, and yet I don't want to SSH into my phone and make && sudo make install. God help my non-techie family and friends. We all want to install software that has gone through at least some semblance of an audit, and we want to install it trivially, and we want that software to then run in a minimally privileged sandbox. While a "phone app" is technically "software", the distinguishing factor is that aforementioned features are implied when someone says "app".

So recap of what it's like to use a phone app:

- There's a distribution channel that makes it easy to search for and install the app of your choosing.

- The apps run in a sandbox. Privileges/capabilities are surfaced through a common setting UI: don't want the app to be able to send you notifications? You can toggle that in a way that the app can't circumvent.

Maybe you do or do not appreciate these features. But if you, like many, do appreciate these features, the a logical question is: is there anything like this but for software we want to use outside the context of our phones? And, well, if we're wanting this software to be accessibly not only on our phone, we should probably make this as maximally accessible as we can, and what could be more accessible than a browser based app -- now you're not limited to using it just from your phone (though that's possible too), you can access it from your laptop, or a friend's computer, or your gaming console's browser, or anywhere else where you can browse the web.

So that's what Sandstorm is, more or less. Trivially installable apps, where you can completely own your own data (if you're hosting yourself), and even if someone else is hosting Sandstorm you can trivially export your data, the whole app is sandboxed, you have a common dashboard for controlling each app's capabilities, etc.

cstrahan··on Text Editor Data Structures
The original quote for reference:

> So one Unicode character can be up to 5 bytes long and take up the same canvas space as 3 characters.

FWIW, I didn't read that as suggesting an upper bound of 5 bytes, but rather as an example using arbitrary numbers: N bytes of code units could, depending on the font providing the glyph(s) for the respective grapheme(s), could be rendered at M times the size of, say, the letter A, where N != M -- despite the font otherwise being monospaced. Which is just another way of saying that you must consult the font for the character widths involved.

I think you're reading that quote as an assertion that:

    For any grapheme G, G can be encoded in at most 5 bytes.
While what I think was being said was:

    There exists a grapheme G, where G is encoded in 5 bytes, and the respective glyph happens to be displayed at 3 times a single character (e.g. the letter A), despite the font otherwise being monospaced. Therefore you *must* consult the font for each glyph to correctly determine character widths.
cstrahan··on Csexp: S-Expressions over the Network
How would one print a function with a free variable? Consider this (but imagine there is surrounding code which defines y):

    (lambda () y)
How can we print this in such a way that preserves semantics?

You can’t just print the original source definition, as y would then be undefined (or I suppose it could be defined to be whatever y is in scope at the deserialization call site or something thereabouts, but that wouldn’t guarantee that the deserialized function is equivalent to the other with respect to how it behaves, despite the source being identical).

You can’t just inline the serialization of y, because the original function returns a particular reference, and there’s no guarantee that the rest of the dependent code will not expect a stable, unique (and possibly mutable/state-full) instance of this value.

I suppose you could, on the side, also serialize the entire program state (or rather, at least the stack and heap), but then things get tricky when any object refers to something like an opaque pointer (even if you have an extent you can safely copy, you’d have to handle issues like different virtual addresses being mapped) or file descriptors, etc.

But maybe all those challenges can be overcome — so I’ll ask: how would you go about serializing an arbitrary object in such a way that one could deserialize an equivalent object?

cstrahan··on Sandstorm: Open-source platform for self-hosting web apps
Nope. The service, like any other, is them hosting the software (Sandstorm) and the software/apps you choose to run therein.

It’s also open source, so you can choose to host Sandstorm yourself, instead of using their service.

You didn’t ask what Sandstorm is, so I’m assuming you already understand what it is.

cstrahan··on I'm Not a Passionate Developer
> How did you know if they were "passionate" or not? I don't think being "9-5" is a good litmus test.

The Latin root of the word passion is patior, which means to suffer or endure. And I think that’s significant: having an affinity or appreciation for a thing isn’t the same as being passionate.

In the same way that I don’t see how working for (and being compensated for) 8 hours a day can be described as suffering, I don’t see how “passionate” is an appropriate descriptor for someone who only programs between the hours of 9 to 5 For their job.

When someone says they have a passion for programming, I usually take that to mean that their evenings are dominated by programming time, instead of hanging with friends, other hobbies, etc.

And don’t get me wrong, I don’t think people necessarily should be passionate about programming (nor expected to be).

cstrahan··on CPR's true survival rate is lower than many people think
Can’t you be both deaf and healthy? Or missing a leg and healthy? I don’t see how “disabled” implies “not otherwise healthy”.

I often run into people who are deaf, or an amputee, or have type 1 diabetes, etc, but I rarely come across someone that (to my knowledge) has a substantial illness. But I live in a city where I very, very rarely run into someone much over 40 (at that point they are likely to have been married and had children, and the thing nearly everyone does here is move into the suburbs once they’re married with children).

I suspect that where you live makes a huge impact on whether or not you’re surrounded by unhealthy/ill people.

cstrahan··on How to Get Started with Tree-Sitter
I believe they use semantic (Haskell program that uses tree-sitter) for navigation: https://github.com/github/semantic

So the answer may be that semantic does not yet have support for the language in question.

cstrahan··on Fuck being productive
By the time I have offers in hand I’ve deemed each company to be worth working for, so there is no matter of one job being “worse” than the other — they are all acceptable. If there was something I disliked I would have terminated the hiring process.

I guess if someone has a fixation on some particular technology or industry then that could be a distinguishing factor. But I’m motivated solely by the capacity for me to make a positive impact and be appreciated. However, any company that isn’t completely dysfunctional will (at least, from the outside, appear to) check that checkbox.

Maybe I have just had good luck with my interviews so far and I am thus taking (at the point in time that I’m choosing between competing offers) some critical thing that usually varies between employers for granted. I dunno.

cstrahan··on Fuck being productive
> employees with internal motivation are miles ahead and more productive than without one.

Isn’t that invariant with respect to (prospective) employer?

I naturally feel motivated to do a good job and earn the respect of my coworkers, and the satisfaction and pride that comes with a job well done. In addition to my work output, the title I hold and the salary I’ve earned are evidence to my friends, family and romantic interests of my drive to generate value and the expertise I’ve tirelessly worked towards developing. This further motivates me do good work.

I don’t see why “internal motivation” would vary between employers. Could you give examples of attributes that contribute to your own level of motivation?

cstrahan··on Understanding Passkeys
Are you implying Pass is proprietary?

In case clarification is needed: Pass is GPL licensed, open source software.

GPL is not the same as the GNU Project. The former is a license; the latter is collaborative project composed of multiple software packages and their respective contributors, typically using the GPL license.

I fail to see how the authorship of a project makes something (non)proprietary — in all definitions I’ve seen, it’s the terms under which software is permitted to be shared that determines if something is proprietary or not.

cstrahan··on Understanding Passkeys
Per this: https://www.slashid.dev/blog/passkeys-deepdive/

The private key is both kept in your phone’s Secure Enclave and stored in iCloud, so strictly speaking the implementation isn’t hardware-bound in that case.

But I think the intended point is something more practical: can you, as a user, export the passkey to be shared on your non-Apple laptop, phone, etc? And maybe I’m mistaken, but I’ve been under the impression that you cannot.

cstrahan··on Fuck being productive
Are you assuming “money” isn’t also the answer to this implied question?
cstrahan··on GitHub switches to Rust based Blackbird code search
All of the above, and also:

If it’s written in Rust, the tooling (Cargo; the Rust language server; etc) make it much easier than most other languages to jump in and fix something or add a new feature. I’ve contributed to projects written in Ruby, Python, Java, C#, C++, Haskell and many others, and every Rust project I’ve ever touched has been much easier/quicker to go from `git clone` to PR submitted.

That may not be appealing to people who never fix or extend the software they use; for me, as someone who finds bugs and deficiencies in almost everything I lay eyes on, knowing that a piece of software is written in a language that is the least likely to frustrate me and suck up my time when I inevitably have to dive in and tweak something is a huge competitive selling point over similar software written in other languages.

cstrahan··on Standardizing BPF
> the device itself calls back into a BPF function in the NVMe driver

This statement contradicts itself. A driver (a “kernel module” in Linux lingo) runs in the kernel, and sure, that driver can call out to BPF or whatever else it desires, but then that isn’t the device doing so, that’s your computer (running your Linux kernel, in turn executing the aforementioned kernel module / driver) doing so.

Restated from another perspective, drivers don’t run on devices. Something may run on devices too, but that’s different, and we’d call that something like “firmware”.

Edit: the intended takeaway being that device manufacturers/designers should have little to gain from BPF being standardized (unless that BPF is being executed on the device, as this now implies an API contract between device and host) — a driver can always declare that the semantics is whatever the Linux kernel does and call it a day.

← PreviousPage 8 of 17Next →