But essentially, when it comes to union types, they behave like sets. The compiler merges them. (A | B) | (A | B) is the same as A | B. But for sum types (even anonymous ones such as tuples) the compiler can't merge them because that would lose information (if the result is from the first A | B or the second one). Instead, you end up with a nested structure.
Which one is desired depends on the use-case, but it's definitely different.
> It's impossible for the compiler to do anything with x without adding some kind of runtime type tag.
Yes, in this particular case that's true. If the developer tries to do anything except maybe printing it for debug then the compiler will tell the developer so and the developer will have to switch to sumtypes / wrap it just like it is done in OCaml by default.
But essentially, when it comes to union types, they behave like sets. The compiler merges them. (A | B) | (A | B) is the same as A | B. But for sum types (even anonymous ones such as tuples) the compiler can't merge them because that would lose information (if the result is from the first A | B or the second one). Instead, you end up with a nested structure.
Which one is desired depends on the use-case, but it's definitely different.