Hacker Newsnew | past | comments | ask | show | jobs | submit | Guvante's commentslogin

Most of your examples would not be considered harm for another...

Null terminated strings were an intentional compromise, known to be inferior for execution but superior for memory

Null being an "allowed" value for pointers is the mistake e.g. what became nullptr. "Allowed" because garbage values are garbage.


Think of the alternative where we'd be dealing with endless issues because someone though 255 or 2^16-1 characters ought to be enough for everyone.

VB6 :)

I mean when comparing to machines though the only thing the machines are better at is maybe not getting distracted while humans tend to be better at adapting which is usually important for safety to put it mildly

After all the first Tesla auto drive fatality was literally "that is probably a sign not a trunk"...


Object pooling is just malloc that is a little cheaper and maybe a different failure case when you are full...

If you have a good method to handle the equivalent of OOM then they can make a difference in how the program runs but normally they are just a performance optimization.

Honestly with 64 bit addresses it would be nice if address reuse were eliminated but that requires memory movement of a different kind (probably just as dangerous) or some terrible paging work for the OS...


They didn't even get a good photo of the final product...

The only shot I saw was "while I was writing them" why not show what it looks like while in use?


https://wilsonharper.net/projects/businesscard/

This is the post from some days ago that I thought I was commenting on - sorry for the fuckup, please accept this replacement story - mea culpa.


You paint the sponsors in an unnecessarily negative light here.

They want to fund code they will use and that requires updating versions to be doable for like a million not twenty million.

Everyone loses site of the massive sizes of the codebases involved. When your codebase is measured in millions LoC minor breaks can be devastating.

Minor steps are fine and appreciated but "rip off the bandaid" is better left to alternative projects like Rust.


Replace C++ code with Rust is happening at a glacial pace, basically involving full rewrites of the module replaced and significant work to refactor the surrounding code to support it.

(This is why Rust is focusing heavily on interop right now, if it is easier to integrate more projects can use it)

The problem is updating a decently sized program from one version of a compiler to another can take man months to do and that doesn't typically include intentional breaking changes.

A C++ change that requires updating 1% can be several hundreds of thousands of updates which is on the scale of beyond a man year depending on if you can regex cheat.

The fear the author is glossing over (I wouldn't say ignore they acknowledge there are reasons) is when faced with a man year to update people just don't.

Especially when the most interesting breaking changes don't tend to be synthetic (you can just make modules look like code you couldn't write before after all) but instead be subtle changes in behavior.

That means that you won't necessarily even know all the breakages which is a horrifying concept.


Why would you want to invest in a consumable asset class?

Even if Nvidia hit a wall and can't outclass its old chips they still have a significant experation date on them since using them in data center situations destroys them over relatively short time frames for finance (a few years).

Note that for an asset class the fact that they can last longer isn't important, if they are fungible it means investors need to predictably know the lifespan which requires pessimistic usage assumptions.


These are GPU rental price futures, so you won't be investing in the hardware.

The pitch is that, if you know you'll need a certain amount of compute at a certain point in the future, you can lock in the rental price today.

https://www.cmegroup.com/media-room/press-releases/2026/8/11...


I feel like the write up doesn't really engage with the number one solution used

Don't allow the hard ones

Dependency managers tend to just block a huge category of situations that effectively eliminate the entire NP hard space

Type systems similarly are explicitly cordoned off

The trick isn't "do it anyway" beyond you kind of definitionly need to, it is to acknowledge the general problem is "impossible" so either do your best or start eliminating the impossible


Another way to look at it is that in practice N is typically bounded by a large constant, making the time complexity effectively O(1).

For dependency resolution specifically, the set of possible dependencies is probably in the range 100 - 10000 for all ecosystems, even if the number of available packages in an ecosystem continues to grow.


10000?

Wait until you meet pip and liberal requirements.txt


And then you encounter packages with `setup.py` that generates a random list of dependencies on each run. You can't know the dependencies without running code.


A variant:

> Don't encounter the hard ones

For example, with the simplex method for linear programming, we don't do anything about disallowing the hard instances. We just solve the problems as they come in and none of the ones we get asked to solve ever turn out to be hard. (Generalizing, of course.)


Don't allow the hard ones makes the problems P doesn't it?


Kind of the point yes


Exactly!


> Dependency managers tend to just block a huge category of situations that effectively eliminate the entire NP hard space

Can you elaborate on this? Many _try_ to get around this, e.g. Cargo's https://doc.rust-lang.org/cargo/reference/resolver.html#semv..., but it's not quite in P. Nix offloads dependency resolution to *2nix tools. Go's minimum version selection is just a tree walk, but it loses a fair amount of expressivity.


Presumably the ones where you are expected to have the latest version of everything and make a new package if you break that (python2 → python3)

Not sure why more ecosystems don't do this. Sure an update could break dependents, but, like, you already have a big problem if a dependent was keeping you on an old version no matter what.

Building a SAT solver into the package manager seems to be a solution in search of a problem.


> Presumably the ones where you are expected to have the latest version of everything and make a new package if you break that (python2 → python3)

This is essentially Go's MVS. (Can only specify a minimum bound, can't depend across major version bumps). Any others? MVS is more the exception than the rule.

> Not sure why more ecosystems don't do this. Sure an update could break dependents, but, like, you already have a big problem if a dependent was keeping you on an old version no matter what.

I'll bite :-) You lose a lot of expressivity. Incompatibility is specified from the dependee, despite being a property of the dependency. I can point to instances where this has causes issues; e.g. dependees using unstable APIs. That's exactly why MVS uses the _minimum_ bound, to minimise such breaks.

> Building a SAT solver into the package manager seems to be a solution in search of a problem.

I agree in that there are better algorithms for error reporting! But NP-hardness is a pretty fundamental property of dependency resolution and removing it moves the pain somewhere else.


> Building a SAT solver into the package manager seems to be a solution in search of a problem.

I can't tell you which ones off the top of my head, but I'm sure a number of package managers do use constraint solvers to find dependencies matching the constraints.


Most of them do and that's why they are complicated and unreliable.


I thought Cargo did a simple SemVer check and then imported both if there was a conflict

My understanding was NP is when you test and rebuild the graph over and over which doesn't need to happen if you isolate or fail


> I thought Cargo did a simple SemVer check and then imported both if there was a conflict

Not quite, Cargo only allows multiple versions of a package when they are semver incompatible (i.e. have different major versions).

If you allow multiple versions of a package you get some quite gnarly errors if you try to share values between them. The rationale of allowing semver incompatible versions is that they should be incompatible anyway.

Cargo has an interesting proposal on private and public dependencies that allows you to say multiple versions are only allowed when they aren't visible from the same part of the dependency graph https://rust-lang.github.io/rfcs/3516-public-private-depende...


I haven't seen any algebraic effects system that is that powerful

The closest is linear types but even then drop isn't an effect but also a function you can call to allow not continuing the references


Yeah I should have phrased that better. The traits themselves are not the effects, they're bits of code which can have side effects. Rust doesn't track those side effects in the type system yet, but !Forget especially is the essence of that idea. If you implement it for everything owned by a function, you can basically infer that the function does not have the leak effect (which would be an effect in the effect row of a Forget trait implementstjon).

If you try treat memory as an effect you gain the need for several polymorphic effect type functions drop, forget, etc which map a type to the effect row charged by its corresponding Drop, Forget impl. Rust doesn't have that type system obviously.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: