While I think I made it fairly clear that my position is that sum types aren't what D should focus on at this time, I have the feeling this is going to be ignored, and that we are going to roll out something regardless, so I might as well put a few things here to make sure we at least end up with something that fit within a language and its spirit.
We already have a sum type like feature in D: enum. Wait what? Well enum, short for enumerations, are types that specify a set of value a variable can take. sum types are just an extension of that idea: instead of simply providing a set of values, they also allow to provide entire classes of allowable values, types.
So we could simply have:
enum MySumType {
int,
bool,
MyStruct,
// ...
}
D is a system programming language, which means one needs to be able to specify how the bits are layed down. Thanksfully, enums already provide us with a syntax to provide the backing type for our enum, and that won't be enough here. But just like ranges, we can duck type the whole thing the following way:
enum MySumType : BackingType {
int,
bool,
MyStruct,
// ...
}
// We assume that MySumType is able to generate some magic enum internally,
// such as MySumType.__Kind is an enum with the different types in there.
struct BackingType {
MySumType.__Kind kind;
union {
This,
That,
TheOther,
}
// opAs is used by the compiler to extract the right union member.
auto opAs(MySumType.__Kind T)() {
// Return the right element in the union.
}
}
The BackingType can be made 100% optional and compiler generated when none is provided.
This is obviously a half assed proposal, as I can already see ambiguity in the syntax thinking of it for minutes, but my goal here is for people to think a bit about what that feature looks like FOR D.
Without a coherent design we only get exploding complexity and things that don't fit together. We have an exemple of that in D currently that is definitively affecting that design (which is why we are finding ourselve looking at introducing a new feature rather than generalizing an existing one): D is completely schizophrenic about whether enum are a closed or an open set. final switch
assumes closed, but binary operators such as |
assume open.
Well, if forces us to answer that question (or to ignore it and add to the pile of bear traps) so I will answer it: the set of allowable value must be closed, as this is the construct that naturally extends towards supporting sum types.
But you'll ask me, we have these open set, what do we do about them? We do this:
enum CloseSet { A, B }
enum OpenSet { A, B, ... }
Voila, problem solved. When using the first one, operator like |
either fall back to the base type, or plain don't work. When using the second one, final switch must, in addition to handled all the value, provide a default label.
Voila, now what about pattern matching these thing? Like this: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2392r0.pdf