11 comments of 47

[ 3.1 ms ] story [ 25.9 ms ] thread
Make your invalid states unrepresentable
this explains virtually everything:

- that's why OOP failed - side effects, software too liquid for its complexity

- that's why functional and generic programming are on their rise - good FP implementations are natively immutable, generic programming makes FP practical.

- that's why Kotlin and Rust are in position to purge Java and C, philosophically speaking - the only things that remain are technical concerns, such as JetBrains' IDEA lock-in (that's basically the only place where you can do proper Kotlin work) as well Rust's "hostility" to other bare-metal languages, embedded performance, and compiler speed.

While the reasoning holds generally, that specific example is wrong. The type the author presents is not the “User Profile” but a “User Profile Load Resource” (or something in those terms).

When you actually design interfaces you discover that there are way more states to keep in mind when implementing asynchronous loading.

1. There’s an initial state, where fetching has not happened yet

2. There may be initial cached (stale or not) data

3. Once loaded the data could be revalidated / refreshed

So the assumption that you either are loading XOR have data XOR have an error does not hold. You could have data, an error from the last revalidation, and be loading (revalidating).

That’s why I aim for my code to be custard-like.
Nice story.

Now, is that gonna help design better software in any particular way?

   What does it mean when loading is false, error is Some, and data is also Some? The type allows nonsense. You write defensive checks everywhere. Every render must guess which combination is real
How about you never write the wrong state in the first place ? Then you have nothing to take care of
Once you notice the pattern, you see it everywhere:

> Stability isn’t declared; it emerges from the sum of small, consistent forces.

> These aren’t academic exercises — they’re physics that prevent the impossible.

> You don’t defend against the impossible. You design a world where the impossible has no syntax.

> They don’t restrain motion; they guide it.

I don't just ignore this article. I flag it.

In theory, I think the article is correct.

Yet in practice, I've found that it's easy to over-model things that just don't matter. The number of potential states can balloon until you have very complex types to handle states that you know won't really occur.

And besides, even a well modelled set of types isn't going to save you from a number that's out of the expected range, for example.

I think Golang is a good counter example here -- it's highly productive, makes it easy to write very reliable code, yet makes it near impossible to do what the author is suggesting.

Properly considered error handling (which Go encourages) is much more important.

That’s one of the reasons why I love Swift. Representing and using a state is easy and super readable. It is an incredibly good language.