So some good discussion has occurred since my last post. I proposed an idea late at night (which I really should have slept on) and I think that caused some obfuscation between sum types and union types.
I am personally in favor of a minimal sum type implementation, similar to the one in post 36. Where you match on top level types with index matching, but type matching for top level types is syntactic sugar.
let _: (A | (B | C)) = ...;
match _ { // match _ {
a : A => ..., // 0(a) => ...,
bc : (B | C) => ..., // 1(bc) => ...,
} // }
However, many people in the union type camp really don't like the ergonomics of this approach, as they would like the ability to match on the discrete types. Not only that, if duplicate types occur within a anon-enum, they would like to be able to match on all duplicates as well. The following is not valid sum type, but would be valid union type.
// Note the duplicates and the nesting
let _: (A | A | (B | C)) = ...;
match _ {
a : A => ...,
b : B => ...,
c : C => ...,
}
Sum types could not use type matching in the above case, you would have to resort to using index matching or coercion.
Coercion can achieve a similar effect, because sum types would support coercion into a more normalized form, meaning you could write the above using sum-types like the following
let not_normal: (A | A | (B | C)) = ...;
let normal: (A | B | C) = not_normal; // coerce into a normalized form
match normal { // match normal {
a : A => ..., // 0(a) => ...,
b : B => ..., // 1(b) => ...,
c : C => ..., // 2(c) => ...,
} // }
So as an appeal to those in the union type camp, I had the idea that maybe a type match could be a coercion site into a sum type described by branch arms. Essentially making the above coercion implicit. This was problematic as I discovered right after posting that this would force a move and matching on references wouldn't be allowed.
So coercion is off the table, but the logic built for coercion could still be useful in this situation. Sum type coercion is unidirectional, you can only make a sum type more normal. What this means is you create a many to one mapping from the non-normal rhs to a more normal lhs.
// LHS = RHS
(A | B) = (A | A | (B | A))
// Creatins this mapping
(A | B) = (0, 0, (1, 0))
Now if we take our many to one mapping, and flip it to a one to many mapping, where each mapping is a multiple branch arm, we could potentially create the following match.
let _: (A | A | (B | A)) = ...,
// faux coerce _ into (A | B) to get the mapping
match _ { // match _ {
a: A => ..., // 0(a) | 1(a) | 2(1(a)) => ...,
b: B => ..., // 2(0(b)) => ...,
} // }
Before we said that type matching only works for sum types when unambiguous and the top level types are matched exactly. But now we could say that: essentially type matching works whenever the sum type is coercable into the match arms.
Now this still would still be based on the locally defined types.
let _ : (u8 | u8 | T) = ...,
match _ { // match _ {
u: u8 => ...,// 0(u) | 1(u) => ...,
t: T => ..., // 2(t) => ...,
} // }
I don't think this would break sum types, and this feature seems to be important to those who argue for union types. If this is valid, and I am not missing something, this could be an addition as an appeal to those in the union type camp. While still being consistent with sum types as type matching is sugar anyways.
Sorry I blurt out a million snippets, I'm an example oriented person.