6 ms·
So you're talking about untagged unions? > This is useful as a shorthand when you don't want/need a new type to represent your problem, similar to tuples. Yes
by bPspGiJT8Y 3y ago
So you're talking about untagged unions?
> This is useful as a shorthand when you don't want/need a new type to represent your problem, similar to tuples.
Yes this is handled perfectly by the generic sum type, you don't need untagged unions for this. Rust used to have Either in its standard library, but they removed it and kept Result only. Semantically they're the same (a ⊕ b) but Result's name implies it has something to do with some "results". Anyways nothing stops you from creating one yourself, or even using Result if you're fine with the weird-sounding name.
- duped 3y agoNo, I am not talking about untagged unions. I don't understand your notation. To be concrete, I am talking about tagged, disjoint union type, that does not require naming a new type to use. This is also not covered by the Either/Result type. Rust supports untagged unions, but they cannot be matched (because they have no tag). An anonymous union would still be tagged internally, but would be less general purpose than the generic enum type.
- bPspGiJT8Y 3y ago> To be concrete, I am talking about tagged, disjoint union type But you just said "For example `A | B | A` is the same type as `A | B`". How would this be possible for tagged union types? > that does not require naming a new type to use > This is also not covered by the Either/Result type It's more probable that I'm just not understanding what you're talking about, but *the only* re-usable tagged union type similar to tuples is *the* sum type. Let's say you're dealing coffee. People want it either with sugar or without sugar. You don't want to create a new sum type CoffeeFlavor? Fine, just use Either<Sugar, NoSugar>. This is *the* equivalent of a tuple. You need more than 2 options? No problem, Either<Sugar, Either<JustABit, NoSugar>>. I don't know what else could be a "anonymous tagged union".
- duped 3y agoAh ok I think we're mixing up terms here - in the context of systems programming languages, a "tagged" union refers to an integer in front of a bag of bytes that holds the data of the "un tagged" union. Rust has both tagged (enums) and untagged (unions) union types. What you're asking about is a discriminated vs non-discriminated union, and indeed, that's exactly what I'm talking about. A | B |C is not the same type as Either<A, Either<B, C>> because Either<A, Either<A, B>> cannot type check as Either<A, B>. But even if you want to argue that you can represent things that way, it misses the point. The goal is to remove complexity from the type hierarchy of the program, not add to it.
- bPspGiJT8Y 3y ago> because Either<A, Either<A, B>> cannot type check as Either<A, B> Why would you want the former to type check as the latter? Where do you see the complexity?
- k_g_b_ 3y agoBecause your code might not need to care about the position you insert your A or B (left/right for Either), you also might not care whether it's an (encoding as) Either<A,B> or SomeoneElsesEither<A,B> and you also don't want to have to deal with flattening nested Either's as in the example. These types are also called "set-theoretic" types as A|B means exactly the set of all values that can be typed as A or typed as B - note that this also induces a whole subtyping rule by set inclusion and this is in contrast to sum types where a value typed Either<A,B> can never be typed A or B - to move between them you need to apply extractors/match/constructors/(not sure of standard type theory nomenclature). Implementation of these union types might still need additional tags and construction/matching/extraction underneath, but from a programming perspective there's less complexity as compared to involving an additional named type Either (or EitherOf3 and EitherOf4 and ...) and manually implementing set-theoretic laws.
- bPspGiJT8Y 3y ago> Because your code might not need to care about the position you insert your A or B This is understandable. But what does it have to do with "collapsing" `a | a` into `a`? Throughout your post I think you're talking about plain untagged union types but that's something the guy I've been replying to already ruled out. Position problem can be handled beautifully by variants based on row polymorphism, such as in OCaml or PureScript. There you can access the fields not by their position but by a key, like keys in objects in JS, meaning that they don't have to be ordered at all. It's like an inverse of a struct: in a struct all fields/keys are guaranteed to exist, but in a variant only one of them exists. Due to row polymorphism they can also be extensible. You can even "handle" a particular field/key and remove it from the type but keep all the other ones and delay handling them. > you also might not care whether it's an (encoding as) Either<A,B> or SomeoneElsesEither<A,B> This is a theoretical issue but in practice I don't think I've ever seen anyone using some non-standard Either-like datatype in languages I've dealt with. Where Either needs to be used people just use Either. > and you also don't want to have to deal with flattening nested Either's as in the example What would "flattening" mean here? Fundamentally there are only 2 operations you can do on a generic sum type like this: either inject a value (construct the type) or try to get the value at a certain position. You might also think pattern matching will get tedious, but that's not the case either, you can just have a function `actOnAorBorC` and call it with `actOnA`, `actOnB` and `actOnC` and do the pattern matching inside these functions.