(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
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.
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`.
> 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.
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.