6 ms·
> Here’s a classic example: a data type representing the state of an API request, which can be loading, loaded, or in an error state. If it has loaded, it has t
by abc-1 2y ago
> Here’s a classic example: a data type representing the state of an API request, which can be loading, loaded, or in an error state. If it has loaded, it has the expected data associated with it, and if it is an error, it has some descriptive error. A naïve implementation simply puts those all on a single data structure, with booleans for the states and optional value and error fields:
Nope, not naive at all. Not every single thing needs to be a richly expressed type system. I actually don’t like to see a million different types for some trivial state machine. It is appropriate at times and inappropriate at other times.
Not trying to sound like a grumpy old man- but juniors read things like this and then want to make a bunch of complex types to make “errors unrepresentable” when it’s totally unnecessary and complicating in many cases.
- tomjen3 2y agoReally? You need one type “either”, which then wraps your return type or an error as appropriate, giving you compile time guarantees. That’s not a million little classes, that is one class.
- Terr_ 2y agoPerhaps an intermediate position be: "You can use a loose dict/map where a different subset of keys/values are in-use for different situations, but there should be a single specific discriminator key-value which is always set, one that unambiguously tells people which form is being used." That's in contrast to logic like: if result.data is not None: # Look for successful results elif result.error is not None: # Log or handle error elif result.background_processing: # Oops, optional asynchrony else: # Must be in-progress, right?
- valenterry 2y agoIn what cases would that be complicating things? It usually simplifies things. The only cases that I can see where it might complicate things is if your programming language is too low level or un-ergonomic to properly express or work with sumtypes/uniontypes.
- eru 2y agoWell, Java about 20 years ago would have made this kind of thing really hard. (Or Go about 10 years ago. I don't know if these languages got better in the meantime, I'm just reporting on my experiences from back then.)
- valenterry 2y agoJava today still makes this kind of thing fairly hard actually (well, at least they have some kind of sumtypes and half decent pattern-matching now). Go is even worse.
- freeone3000 2y agoLanguages with sum types, like Haskell and Rust, make these things trivial.
- marcosdumay 2y agoIf you are stuck with Java, you will not want to offload that kind of checking into the static types system. Instead, you have object constructors. Use them. They will dump all kinds of `if (result.is_valid)` variants over your code. But well, this is Java. It's not a different kind of shit from `if (result != null)`, and it's something you have to live with.
- eru 2y ago> I actually don’t like to see a million different types for some trivial state machine. It's just one type, a sum type.
- deleted 2y ago[deleted]
- klysm 2y agoI guess we have different ideas of what complexity is. I like it when the compiler doesn’t let me write incorrect code
- sevensor 2y agoI disagree. Constructing a data type that can be in an invalid state is packing powder in the shells for your footgun. I love a trivial state machine represented with types. Trivial code is wonderful; I have bigger fish to fry than keeping track of states represented implicitly. That being said, there’s a real tendency to take “make illegal states unrepresentable” the wrong way. I see it used as an excuse for sheer laziness, resulting in code that takes a needlessly narrow view of what constitutes valid input and handles errors in the most graceless possible way. Just because some data isn’t a valid input, doesn’t mean dropping it on the floor is always the right thing to do. Take handling json as an example. There are at least three layers. 1. Is it syntactically valid json? 2. Is it structurally valid? Objects have the expected fields, arrays are not scalars, and so on? 3. Is it semantically valid? Numbers are within bounds, strings are not too long? For each layer, the appropriate response is different, and for each layer there’s an appropriate representation for failure. That is: failure is a legal state, and it must be representable. Confusion about this has led to some pretty poor software of late.