3 ms·
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 a
by marcosdumay 2y ago
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.
- nikajon_es 2y agoI 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
- marcosdumay 2y agodata 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.
- looperhacks 2y agoThe "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
- marcosdumay 2y agoYeah, that's the bad definition that leads to problems like the one the OP describes.
- gf000 2y agoYou 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`.
- marcosdumay 2y agoYes, 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.