> [Most production incidents] are due to the code entering a state that should never have been possible.
You say:
> [...] it is more true that most production incidents are due to the system entering into one of thousands of unsafe states which were possible and latent in production potentially for years
I see you both agree that a broken system enters an "unsafe state" (your words) or a "state that should never have been possible" (OP's words).
"Unsafe state" and "state that should not have been possible" are, in practice in a real system, the same practical thing. I suspect you both would agree "system confuses a string for an integer and acts based on erroneous value" or "system acts on internal state that indicates the valve is both open and closed" would be states that a system should not be in. Outside pedantry, your descriptions are practically synonymous with each other.
The crux is in the "never have been possible" bit. In complex systems, it is impossible to eliminate these potential states with functional programming or any other technique, unsafe states are always potentialities that must be actively controlled.
Another way of casting it is like this. The goal may be:
1. Eliminate possibility code can enter invalid state
2. Control parameters of the system so that it remains in a safe condition
I agree with you: no matter how good of a job the code (by construction or types or otherwise) does of “making unsafe states unrepresentable”, that in no way makes a real world complex system “safe” by itself.
Code can be structured so that valves may only be open OR closed, but nothing stops the real world from returning a sensor reading that says “the valve is <undefined>”. To remain a “safe” system, the system must deal with inconsistent states like “heisen-valves”.
I've released version 2.0 of roughenough, a Rust implementation of the draft IETF Roughtime protocol (https://datatracker.ietf.org/doc/draft-ietf-ntp-roughtime/). The Roughtime protocol provides cryptographic secure time synchronization with detection of server malfeasance.
Release 2.0.x of roughenough is a rewrite of the implementation to be simpler, more comprehensive, and more performant. The repo has multiple crates that implement:
* Performance-oriented async UDP server
* Command-line client with multiple output formats
* Multiple backends for secure key management for the server
* Clients can (optionally) report malfeasance to a remote server for analysis
> In that sense, Rust enables escapism: When writing Rust, you get to solve lots of 'problems' - not real problems, mind you, but fun problems.
If this is true for Rust, it's 10x more true for C++!
Lifetime issues are puzzles, yes, but boring and irritating ones.
But in C++? Select an appetizer, entree, and desert (w/ bottomless breadsticks) from the Menu of Meta Programming. An endless festival of computer science sideshows living _in the language itself_ that juices the dopamine reward of figuring out a clever way of doing something.
Came here to comment on the same thing. I've never been able to articulate this as well as the author did, and it is so true! Every programming language requires you to solve some puzzles that are just in the way of the real problems you are trying to solve, but some much more than others.
People have compared Rust to C++ and others have argued that they really aren't alike, but I think it's in these puzzles that they are more alike than any other two languages. Even just reading rust code is a brain teaser for me!
I think this is why C and Zig get compared too. They apparently have roughly the same level of "fun problems" to solve.
Relacy is notable for being the only one in c++ that knows what a fence is, at least last time I looked.
It told me a structure might not make forward progress on some thread interleaving. I.e. if only one thread ever runs, the others don't do anything. That's true but uninformative. I wasn't able to coax it into using a vaguely fair scheduler to get more useful information out than that.
Related: the notion of "safety" (refusing to discuss "harmful" content) is rather shallow and (relatively) easily undone: _Refusal in Language Models Is Mediated by a Single Direction_ [https://arxiv.org/abs//2406.11717]
At least try to be polite to the cache coherence system, please. Do a loop where you check (with a plain relaxed (when available) read) whether the compare and swap should work and, if not, do your platform’s pause operation (REP NOP on x86) and try again. Only do compare-and-swap if the optimistic read thinks it will work.
The average duration between any two moon phases is 29.530589 days.
Many watches use 29.5 days between phases for simplicity. This results in the watch's moon phase display being off by one day for every two years the watch operates.
There are a few watch manufacturers that use epicyclic gear trains to make the moon phase calculation more precise. An example: the Ochs und Junior moon phase watch will operate for 3,478.27 years before its moon phase display is off by one day (https://www.ochsundjunior.swiss/watches/moon-phase/).
Yes, my impression is that Go was created partly to be used in corporate environments to replace Java with a less verbose language that scaled well, and it seems to be doing well in that regard.
Sadly, it ends up far more verbose than Java when you have (as I did after trying to port a project from java to go) over 200 copies of the same observable, sorted, copy-on-write set implementation, because Go doesn’t have generics. And then you change one thing in one place, and gotta change it everywhere again.
And how do I get the same type out that I put in? I'd end up with Java 1 style programming, no generics, and having to cast atuff from Object/interface{} everywhere.
interface{} is like Object, if it even exists once in your code, it's broken.
A couple reasons that wouldn't work well (there may be more, I haven't thought about this much):
1. Using an interface would nuke your performance. Data structures where every data structure operation requires one or more vtable lookups on items in the structure can really hurt your time and space performance. To get good performance, you need true type-level generics so the compiler can specialize at compile time.
2. Anyone could put anything that followed the interface in the data structure, even if it was of the wrong type. To get any form of static type safety, you'd still have to define a wrapper type and a bunch of functions for every type you'd want to put in the data structure. You could save a bit of work by re-using your (slow) code you wrote generically using interfaces.
OP says (your quote):
> [Most production incidents] are due to the code entering a state that should never have been possible.
You say:
> [...] it is more true that most production incidents are due to the system entering into one of thousands of unsafe states which were possible and latent in production potentially for years
I see you both agree that a broken system enters an "unsafe state" (your words) or a "state that should never have been possible" (OP's words).
"Unsafe state" and "state that should not have been possible" are, in practice in a real system, the same practical thing. I suspect you both would agree "system confuses a string for an integer and acts based on erroneous value" or "system acts on internal state that indicates the valve is both open and closed" would be states that a system should not be in. Outside pedantry, your descriptions are practically synonymous with each other.