News for hackers. Stuff that matters.
Re:An epic treatise on error models for systems programming ... by marcosdumay on Saturday March 08, 2025 @10:53AM (#43301122)
Oh, here comes my usual rant about "algebraic" sum types that break all of the algebra rules on sums...

On those languages, if you define the types X = A + B and Y = A + B, and if you try to match X = Y, they don't match at all. Besides, they don't compose (X = A + B, Y = X + C, Y = A + B + C is false).

The result is that if you go and declare `read :: Handler -> Either FileAccessError String`, you can't just declare your function as `f :: String -> (FileAccessError | NetworkError | MyCustomError)` expect it to work well with the rest of your code. Because every time you need a different set here, you'll have to declare a different type, and transform the data from one type to the other.

Re:An epic treatise on error models for systems programming ... by looperhacks on Saturday March 08, 2025 @11:48AM (#43301511)
The "sum" and "product" is more about the cardinality of the type. If you have a type A with 3 possible values and B with 5 possible values, then the sum type X = A+B has 8 possible distinct values while the product type Y = A*B has 15 possible values
Re:An epic treatise on error models for systems programming ... by marcosdumay on Saturday March 08, 2025 @12:54PM (#43302000)
Yeah, that's the bad definition that leads to problems like the one the OP describes.
Re:An epic treatise on error models for systems programming ... by nikajon_es on Saturday March 08, 2025 @11:41AM (#43301457)
I don't understand how they don't compose, from your example:

    (X = A + B, Y = X + C, Y = A + B + C is false)
I understand that types aren't math values, but isn't the point of using a `+` to describe the communicative value of the type so that `(A + B) + C = A + B + C`?

Also

Re:An epic treatise on error models for systems programming ... by marcosdumay on Saturday March 08, 2025 @12:53PM (#43301989)
data A = A

data B = B

data X = XA A | XB B

data Y = YA A | YB B

f :: X -> () f = undefined

let r = f (undefined :: Y)

There's absolutely no way to write this so it compiles. In fact, there isn't even a way to define the composed types so that they only express a sum, you have to add extra baggage.

Re:An epic treatise on error models for systems programming ... by gf000 on Saturday March 08, 2025 @12:53PM (#43301993)
You would want union types for that, they behave more like what you have in mind. (Scala 3 has such a feature, as an example)

Also, Rich Hickey's Maybe Not talk is sort of about this "non-composability" of ADTs. E.g. a function accepting a String can be relaxed to accept `String | None` without recompile, but changing it to `Maybe<String>` would make a refactor necessary.

At the same time Maybe<Maybe<String>> may encode a useful state that will be unrepresentable with `(String | None) | None = String | None`.

Re:An epic treatise on error models for systems programming ... by marcosdumay on Saturday March 08, 2025 @01:10PM (#43302109)
Yes, unions are the proper type sums. Just as fixed cardinality sets are the proper type products, not tuples. I didn't know about this feature in Scala 3, all it seems to be missing for a fully algebraic sum is type negation (like `FileError and not FileSeekUnsupportedError`).

> At the same time Maybe<Maybe<String>> may encode a useful state that will be unrepresentable with `(String | None) | None = String | None`.

We already have the operation that takes a type and converts it into a non-matching type. It doesn't have a well accepted name, in Haskell it's called `newtype` but every language with strict types has it.

There is no need to mix those operations in the fundamental types in a language. The usual emuns and tuples are just less expressive suggared synonyms.