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 The FizzBuzz that did not get me the job
> When the inputs and outputs are types... how do you interface with such a solution?

That wasn’t part of the criteria. If that’s something they wanted to assess, they should have told him “no” when he asked if it would be alright to encode the rules in the types.

Maybe you have an issue with the interview, rather than the interviewee?

cstrahan··on The FizzBuzz that did not get me the job
I think you somehow missed the fact that this wasn’t the standard fizzbuzz, but rather a contrived, convoluted version thereof. Of course they were looking for a clever solution, because any solution that satisfied those rules would be clever by necessity.
cstrahan··on Show HN: Rain hashes – well designed, simple and fast variable sized hashes
Note that you said “likely” and not “equally likely”. Therein lies your answer.

There are applications where hashes are used as identifiers, where it would be impossible to use the original data to resolve possible collisions. One example would be in RTTI (Run Time Type Information), when you want to check if two objects (of unknown-at-compile-time type) are instances of the same type. The only performant way to achieve this is if the compiler emits each type’s RTTI with a unique integer key to efficiently compare against, and this key must be unique across all object files and shared objects. In practice, this is done using hashing. If there is a collision, then the program behavior is undefined, so it is ideal to minimize probability of collision. You can see this issue in the Rust compiler project where there is ongoing discussion about how to handle this hashing, trying to balance compiler speed and collision likeliness:

https://github.com/rust-lang/rust/issues/129014

Another scenario would be like what Backtrace does: a hash is used to identify certain classes of errors in application code. The cost of analyzing each error is quite high (you have to load objects into memory, source maps (if available), perform symbolification, etc), so comparing any two errors every time you want to perform any metrics or retrieve associated data (like issue tracker identifiers) would reduce the performance to a point that any such application would be too slow to be useful. So a lot of thought was given to producing fast, non-cryptographic hashes (these hashes aren’t shared across tenants, and no customer is going to go out of their way to work out initialization parameters so they can be their own adversary) with a known likely hood of collision. You can read about the hash (UMASH) here:

https://engineering.backtrace.io/2020-08-24-umash-fast-enoug...

cstrahan··on Nobody gets fired for picking JSON, but maybe they should?
Hey, if you like living your life uninformed of the risks of your actions (or maybe it’s that the risks are irrelevant to you, if you’re not getting any?), you do you.

I don’t know how I can help you understand the utility of analogies (you recognized the rhetoric as such, yet simultaneously seem stuck on the fact that analogies don’t establish a literal connection). However, I’m not sure if that’s actually the problem, or if you’re feigning confusion in attempt to offend me. Granted, it would seem an odd choice for you to claim incompetence as part of an attempted insult.

Regardless, I’d be happy to help you in any way I can.

cstrahan··on Nobody gets fired for picking JSON, but maybe they should?
To add onto the sibling comment:

It is indeed a play on the saying “Nobody ever got fired for buying IBM”. What wasn’t explained, though, is the meaning behind the expression. It’s an expression used to mock use of the bandwagon fallacy and a general lack of critical thought in the decision process.

If you’re responsible for choosing a vendor, the easy out is to pick one of the largest vendors available: if it’s what everyone else is doing, then you can waive away any personal accountability for any resulting failures by claiming that “it’s what everyone else is doing, so it must have been the right choice, and therefore the failure that occurred must have been inherent to the problem domain, rather than something that could have been avoided if I personally chose better”.

The phrase now gets used to mock such bandwagon behaviors: a CTO completely unfamiliar with Kubernetes, yet choosing it “because everyone else is using it”; an engineer picking a serialization format based on popularity while never having read the spec; etc.

The article isn’t suggesting that anyone actually be fired. It is, however, critical of people choosing JSON due to familiarity/popularity, without any critical thought involved in choice (and bandwagon fallacy does not count as critical thought).

cstrahan··on Nobody gets fired for picking JSON, but maybe they should?
A similar argument:

If you look at the statistics, you’ll find that casual, condomless sex is very common. Despite the risks of STI transmission, it often pans out okay: most STIs are readily curable, while HIV has a low transmission rate amongst heterosexual couples (about 0.08% to 0.2% per sexual act) while PrEP is pretty common for homosexual men, and HSV has fairly low odds of transmission outside of an active outbreak (annual odds of transmission amongst couples: about 11–17% for couples with a male source partner and 3–4% with a female partner). Consequently, many people who have casual, condomless sex with many, many partners will end up just fine.

Is it reckless? Well, the connotation of “reckless” is fairly negative, and I don’t see why I should get to judge what two consenting adults decide is an acceptable risk vs reward for themselves (at least, so long as I’m not involved).

However, when we pivot back to the domain of software/engineering: when software design choices are made via bandwagon fallacy, claiming that “it usually works out okay”, I do find that to be reckless. You may be fine with such a carefree approach, but it isn’t really fair to other engineers, users, and stake holders.

It’s not that I believe JSON (or similarly pitfall-laden technology) should be strictly avoided, but rather that the risks and failure modes should be given serious consideration, rather than minimized or outright dismissed. In terms of the STI analogy, it’s perfectly reasonable for two individuals to be aware of the risks, and depending on their appetite for said risk, agree upon the inclusive/exclusive nature of their relationship, as well as whether they exchange test results; what would be ridiculous is to pretend that the risk of infection is zero.

There would be zero value in the article minimizing the design flaws (and resulting footguns) in JSON. The insistence that the article should do so is about as bizarre as someone responding to an article on safer sex by insisting that the article be amended with a note that “… but raw-dogging strangers isn’t too risky anyway, so, like, YOLO”.

cstrahan··on The correct amount of ads is zero
I don’t see how sharing one’s rationale for not being a customer (as in the article) is equivalent to asserting a moral stance, nor do I see any condescension.

Is it that you are generally opposed to critical response? And if so, do you see the irony in you critically responding to another critical response?

cstrahan··on Mise: Dev tools, env vars, task runner
They don’t.

I’m guessing you navigated to the https://mise.jdx.dev/environments.html page and saw the TOML syntax (which looks an awful lot like INI), and confused yourself.

Mise (like a lot of software) uses TOML as the format for its config files (as opposed to something like JSON). Mise reads that config, to automatically export environment variables on a per directory tree basis.

When the docs refer to environment variables, they very literally do mean environment variables. The values of which are taken from a format that resembles INI, as you have noticed.

cstrahan··on Mise: Dev tools, env vars, task runner
> Because most people barely know in depth the packaging challenges for one ecosystem.

I think you’re greatly overstating the problem, at least insofar as it relates to this tool.

For example, Python has its prefix (where packages are installed) baked into its installation. pip, ux, poetry — whatever — are going to install python packages there.

This tool is unconcerned with package installation — it is only concerned with getting the interpreters installed and managing which one is on your $PATH.

There’s literally nothing to leak.

And regarding “wraping existing tools” as proof of some shortcoming in mise (and/or similar) — if they reinvented the wheel, that’s where things could leak. And separation of concerns is a good thing.

cstrahan··on Making a parallel Rust workload 10x faster with (or without) Rayon
You don’t need non-libc dependencies to run into issues when copying a binary from one host to another: glibc uses symbol versioning, so (unless you jump through some hoops) a binary built one a machine with a newer glibc will fail to run on a machine with an older glibc.

I ran into this last week — I wanted to install the neovim text editor from the official binary distribution, but because the project’s CI distro has a more recent glibc installed, the executable would fail to run on Ubuntu 18.04.

There are plenty of great details spelled out here, in case you aren’t familiar with this kind of thing: https://stackoverflow.com/questions/57749127/how-can-i-speci...

cstrahan··on School did nothing wrong when it punished student for using AI, court rules
> By your argument, since an encyclopedia is not a person, I can copy it with impunity.

I don’t see where they said (or implied) that.

How does “that isn’t plagiarism” imply “I can copy it with impunity”? Copyright infringement is still a thing.

Have you conflated plagiarism with copyright infringement? Neither implies the other. You can plagiarize without committing copyright infringement, and you can violate copyright without plagiarism.

cstrahan··on D-Link tells users to trash old VPN routers over bug too dangerous to identify
Ha, several days after hearing about CVE-2024-10914 (https://nvd.nist.gov/vuln/detail/CVE-2024-10914)

> Description > > A vulnerability was found in D-Link DNS-320, DNS-320LW, DNS-325 and DNS-340L up to 20241028. It has been declared as critical. Affected by this vulnerability is the function cgi_user_add of the file /cgi-bin/account_mgr.cgi?cmd=cgi_user_add. The manipulation of the argument name leads to os command injection. The attack can be launched remotely. The complexity of an attack is rather high. The exploitation appears to be difficult. The exploit has been disclosed to the public and may be used.

The "The exploitation appears to be difficult" bit, AFAICT, is solely based on the presumption that most users won't put their NAS on the internet.

First heard about it was from the "Low Level" channel on YouTube -- https://www.youtube.com/watch?v=-vpGswuYVg8 -- the bug/vulnerability is pretty embarrassing. And D-Link's approach of "scrap your old purchase, buy a replacement model" is pretty insulting.

Probably going to avoid D-Link products where I can.

cstrahan··on Intentrace: Strace for Everyone
While it doesn't capture every syscall, you can get pretty far with the (seemingly little known) Endpoint Security Framework: https://developer.apple.com/documentation/endpointsecurity

I cobbled together a little tool at my previous job that would capture a full trace of all process fork+exec and file opens, which would then present all of these events in the usual tracing UI (like you'd get when view a trace of a single program with Tracy[1] or Perfetto[2]).

We had a massive monorepo built with bazel and a heap of shell, and it wasn't clear which part of our code base was responsible for generating some mis-generated file. This tool gave me the whole inter-process call stack, complete with arguments and environment variables, letting me quickly find where and how to fix the problem.

It's such a nice tool to have that it kinda blows my mind that no one has openly published anything like it. By leveraging eBPF on Linux, the same UI could be used on both macOS and Linux. If I had more time and energy, I'd do it myself.

[1]: https://github.com/wolfpld/tracy

[2]: https://perfetto.dev/

cstrahan··on It's a Palworld After All a Lawyer Explains Nintendo vs. Palworld [video]
The same could be said of Pokemon vs Digimon. Put the two side-by-side and I wouldn't be able to discern which is which.

Same with Bakugan, Monsuno, Yu-Gi-Oh, Card Fight!! Vanguard, Future Card Buddyfight, Shadowverse, etc.

I don't really see what the problem is. If Nintendo was just a little less out of touch with what their fan base wanted, they could be raking in (even more) cash. But instead of doing that, they waste their resources getting butthurt over the silliest things, like people posting game play videos of humorous single player ROM hacks of games no longer being sold (they have successfully removed more YouTube videos like this than I count by sending DMCA take downs -- its ridiculous). More focus on innovating and less focus on litigation would do them (and their fan base) well.

It's not Palworld's fault that Nintendo et al choose to release horribly unoptimized, not fun Pokemon games (like Pokemon Scarlet and Violet). I think it's great that a competitor in the monster-capture game genre is providing an actually fun game, because despite the enormity of the Pokemon fan base and the massive financial incentive to deliver a compelling game to them, clearly Nintendo either can't or simply won't do it.

cstrahan··on A dictionary of single-letter variable names
Sure, I can do that.

Why the HELL would you need objects and object oriented programming? Wherever you do foo.blah(), you could just do foo_class__do_blah(obj)… so what do objects really grant you that you can’t do with basic procedural programming?

Why do you need Python’s function decorators? You can just manually copy and paste whatever logic you need into each and every function, instead of decorating them.

Why have an iterator protocol? Every datum can have it’s own bespoke way of iteration that you simply have to memorize on a case by case basis (and can not be abstracted over in any common way), why make everything conform to the same interface?

Why have anonymous functions that can close over their lexical scope (what people usually refer to as “closures”)? Consider:

  let x = 0;
  let inc = () => x++;
You could just write:

  function inc(bindings) { return bindings.x++; }
And then remember to pass along bindings whenever you need to call inc. Besides, that’s more or less what’s going on behind the scenes.

I could keep going.

We have these facilities not because they are essential (go program in assembler, and you’ll see how few programming techniques and concepts your processor actually cares about), but because they are nice to have. They are nice to have because they provide us tools for abstraction, so by solving the general case, you solve a host of concrete cases.

Lenses let you abstract over data traversal and projection.

Maybe you don’t appreciate that, and that’s fine. There are C programmers who don’t mind writing 100 different hash tables for different keys and elements. If you are a Python/TS developer, I’m pretty sure you’d find that surprising — if you want a dictionary/hashtable, you just use a dict and call it done — you wouldn’t go build a new dictionary class for each and every key-type/value-type pair.

Different people have different tolerances for the drudgery of solving the same problem for the 100th time.

However: while I don’t understand why someone would choose vanilla ice cream if they’re given the option of chocolate, I wouldn’t act flabbergasted in such a scenario. Why would I? Why should I expect my desires to be the same as everyone else’s?

The only reason I can think of that someone would express shock at someone choosing vanilla over chocolate is if they think that the preference for vanilla indicates something is fundamentally wrong them. Or in other words: being a condescending bigot.

That’s kinda how you’re coming off.

In the event that you were actually asking in good faith: if you like to broaden your skills so you can finish programming work faster, I’d recommend reading up on lenses. If you don’t care about that, that’s fine too, but I’d recommend against expressing shock at the fact that others do care about these types of things.

cstrahan··on HPy – A better C API for Python
Here’s an example of a common Rust dependency which had a very subtle caching issue: https://github.com/dtolnay/thiserror/pull/279
cstrahan··on Code Generation in Rust vs. C++26
> The reflection, code gen, etc. is the answer how you convert the values auto-magically.

I don't think anyone (j-pb included) is saying anything to the contrary.

Here's what you wrote:

>> rusts type system is strong enough to build extremely powerful zerocopy serialization

> Unclear what you mean, but other than syn and quote you don't have a way to reflect and do code gen, outside of build script. Which also use it.

But your response doesn't logically follow from the text you quoted (so I figured you weren't familiar with zero copy). This isn't j-pb saying that Rust's type system could be used to forgo proc-macros -- j-pb isn't saying anything about proc-macros in the text you quoted.

To be clear, these two points from j-pb's original comment are two separate, orthogonal issues:

> - rusts type system is strong enough to build extremely powerful zerocopy serialization, but people don't explore that space because of serde

> - macros and especially proc-macros in Rust are horrible, and are only feasible because of the syn crate

cstrahan··on Code Generation in Rust vs. C++26
>> rusts type system is strong enough to build extremely powerful zerocopy serialization

> Unclear what you mean, but other than syn and quote you don't have a way to reflect and do code gen, outside of build script. Which also use it.

> Note there are alternative like mini serde and nano serde, but no library for type reflection.

Reflection/comptime/codegen are all orthogonal to zero copy (de)serialization.

To briefly(ish) describe zero copy by way of comparison, consider JSON: there's no way to parse a JSON object into your language's data structures without (among other things) copying any strings you come across (rather than directly borrowing a slice of the original buffer). Why not? Consider escapes: you must first unescape the string, which entails a new allocation. But this doesn't stop at strings: if you have an array, you must first parse that array (usually accumulating a copy of the elements in a Vec<Object> or similar, which in turn means that the whole array was effectively copied), rather than providing a "cursor" into the original buffer. Parsing JSON requires traversing the entire buffer and copying everything you come across.

Protocol buffers works much the same way: because structures are variable length (and in fact, scalars are too -- integers are stored as base 128 varints), if you want the Nth element of an array, you have no choice but to parse all the proceeding elements (rather than nudging a cursor's offset by N×sizeof(Elem) -- you can't do that because the size of any element is unknown until after you've parsed it). Because you want O(1) indexing after parsing, the logical implication is that whenever you parse a protobuf message, the protobuf library parses (and copies) the entire thing, and any array/repeated field ends up as a new array allocation in your language (e.g. Vec<Elem>).

Contrast with something like flatbuffers or Cap'n Proto: the code generated from your schema file gives you structures that (more or less) have two fields: a reference to a buffer, and an offset into that buffer. When you do something like person.age(), the offset of the age field (which is constant) is added to the offset of this person record, and that combined offset if used to index into the buffer (something like buffer.read_u32(offset)). Using a library like this gives you performance similar to what you'd have dereferencing array indices and struct fields in plain old data types in your language of choice. You don't pay in memory and processor time parsing everything up front, you only pay for the scalar memory reads you actually use (and a little bit of quasi-pointer chasing, not unlike what would happen with native structs).

Put another way: a zero copy (de)serialization protocol is one where the on-disk format is the same as the (readily usable) in-memory format. This rules out things like string escaping (just store the original bytes), variable sized records/integers, variable length arrays stored inline with records (otherwise that would make records themselves variable length), storing pointers in records (because those pointers will be meaningless when read from disk; instead: store offsets), etc.

You can read more about zero copy as it relates to Rust here:

https://manishearth.github.io/blog/2022/08/03/zero-copy-1-no...

Here's the Wikipedia article:

https://en.wikipedia.org/wiki/Zero-copy

Examples of zero copy (de)serialization libraries:

https://github.com/rkyv/rkyv

https://github.com/google/flatbuffers

https://github.com/capnproto/capnproto

cstrahan··on Show HN: A macOS app to prevent sound quality degradation on AirPods
I believe it’s because macOS uses a worse audio codec: https://gist.github.com/dvf/3771e58085568559c429d05ccc339219
cstrahan··on Mozilla grants Ente- a private Google Photos alternative- $100k
How is Mozilla funding a private Google Photos alternative “competition against a world of Chromium”?

Maybe you didn’t follow the thread close enough — the complaint isn’t that funding Firefox is counterproductive, but that shoveling dollars to a non-Firefox product does nothing to help Firefox compete against Chrome/Chromium, which is kinda hard to argue against, no?

cstrahan··on Refactoring Python with Tree-sitter and Jedi
The fine folks at JetBrains already have done just that:

https://www.jetbrains.com/guide/pytest/tutorials/visual_pyte...

cstrahan··on Refactoring Python with Tree-sitter and Jedi
I’m not a Python developer, but…

I believe the idea is that those identifiers are semantically related: that fixture decorator inspects the formal parameter names so that it can pass the appropriate arguments to each test when the tests are run. A sufficiently smart IDE and/or language server would thus know that these identifiers are related, and performing a rename on one instance would thus rename all of the others.

And maybe you were being facetious, but an IDE is an “Integrated Development Environment”.

Edit: Yep. Took all of 60 seconds to find what I’m looking for, as I type this from my phone while sitting in my throne room: https://docs.pytest.org/en/6.2.x/fixture.html

See the “Fixtures can request other fixtures” section, which describes the scenario from TFA.

And this post describes the PyCharm support for refactoring fixtures: https://www.jetbrains.com/guide/pytest/tutorials/visual_pyte...

cstrahan··on Xkcd 1425 (Tasks) turns ten years old today
In this scenario, is it GP that exhibits the behavior you’re talking about, or you (given that GP gave their own counter analogy) — or both?
cstrahan··on Show HN: Container Desktop – Podman Desktop Companion
The VM you just referred to is a virtual machine, that’s what VM stands for.

I think you forgot how this thread got started:

> If you’re on macOS, then Orbstack is a nice alternative to Docker Desktop

We’re talking about running OCI (“Docker compatible”) images. The page you just linked to makes it apparent that you are talking about something orthogonal: OrbStack’s “machines” feature (https://docs.orbstack.dev/machines/).

The original topic is that OrbStack’s support for Docker containers is fast (implied: faster than Docker for Desktop), which cannot be explained by the lack of a VM, as both use a Linux VM to run one or more Docker containers.

cstrahan··on Show HN: Container Desktop – Podman Desktop Companion
Wrong, Orbstack does use VMs.

https://docs.orbstack.dev/architecture

> OrbStack uses a lightweight Linux virtual machine with a shared kernel to minimize overhead and save resources, similar to WSL 2 (Windows Subsystem for Linux).

cstrahan··on Porting systemd to musl Libc-powered Linux
(I’m guessing this is supposed to be a joke in a similar vein as “Emacs is a great operating system that desperately needs a text editor”.)

You probably are looking for a different word, rather than “indifferent”. Indifference is a property of the observer/subject, not the observed/object. For example, if I don’t care about sports, I might say that I’m indifferent to the outcome of the game my friends are watching.

Perhaps “mediocre” is what you’re looking for?

cstrahan··on A Java language cumulative feature rollup
> Complaining about having to learn basic parts of language, while this is true in any platform...

There’s a difference between obstinately refusing to learn the basics of a thing, versus taking issue with there being a bunch of self-inflicted hoops one must jump through to use a thing.

All other things being equal, I doubt you could put up with a car that required an elaborate 10 minute start up procedure, wherein any mistake pulling a dozen levers, cranking a bazillion knobs, and poking innumerable buttons could result in the engine exploding. If any such car entered the market, approximately zero people would put up with the headache — though I concede that some would likely love such a car, and proudly take the stance that “anyone that doesn’t like it isn’t a real driver”.

The difference is intrinsic vs extrinsic complexity.

When I program, I want to spend as much of my time working on solving the core problem, and as little time doing anything else — whether that’s wrangling unintuitive and poorly documented CLI tools, working around wonky, flaky third party APIs, etc. The value one generates is purely in solving the problem they set out to solve; all of the other hoops they jump through is the cost of doing business. I don’t see how you can fault someone for avoiding tools that (in their estimation) reduces their value to expense ratio.

cstrahan··on Docker Performance Evaluation Across Operating Systems
> I thought the only way to run Linux on macOS was using some sort of Virtualbox-esque solution.

On macOS, Docker uses HyperKit (which is a hypervisor built on top of macOS's Hypervisor.framework) to run a Linux virtual machine behind the scenes, so your containers actually are running in a VM, even if you don't (usually) interact directly with that VM.

cstrahan··on Writing a Rust compiler in C
Cross-compilation is orthogonal to bootstrapping, which is the major motivating factor for having something like what is described earlier in this thread: a small compiler written in C which can compile a subset of Rust, used to compile a larger, feature complete Rust compiler in that Rust subset -- versus what we have right now, which is a Rust compiler written in Rust which requires an existing Rust compiler binary, which means we have an unmitigated supply chain attack vector.

If you change your question to "why does anyone care about bootstrapping?", the answer would revolve around that aforementioned supply chain attack vector.

For details, you could check out:

- Reading the 1984 paper "Reflections on Trusting Trust", for which Ken Thompson was given the ACM Turing award: https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_Ref...

- Or watching this short Computerphile video on the above: https://www.youtube.com/watch?v=SJ7lOus1FzQ

- You can read about the GNU Guix endeavor to achieve a 100% bootstrapped Linux development environment (using zero pre-compiled binaries), starting from a 357-byte HEX file: https://guix.gnu.org/en/blog/2023/the-full-source-bootstrap-...

Perhaps you're comfortable with the lack of assurances that non-boostrapable builds entails (everyone has a different appetite for risk); some others aren't though, and so they have an interest in efforts to mitigate the security risks inherent in trusting a supply chain of opaque binaries.

cstrahan··on I got almost all of my wishes granted with RP2350
I think it makes more sense if you consider the expression "this tickles my fancy".

Why do we use "tickle" there? Because a tickle is a type of stimulation, and "fancy" here means "interest", so one is effectively saying "this stimulates my interest".

If we then consult Oxford Language's definition of pique, we find:

> stimulate (interest or curiosity). "you have piqued my curiosity about the man"

The word "piqued" in "this piqued my curiosity" serves as something along the lines of: stimulated, aroused, provoked

This is aligned with the French word "piquer", as a "prick" or "sting" (much like a tickle) would stimulate/arouse/provoke.

← PreviousPage 4 of 17Next →