Second draft: Sum Type by Struct
Meta
jared771 at gmail.com
Fri Sep 4 07:30:45 UTC 2026
On Thursday, 3 September 2026 at 11:45:12 UTC, Richard (Rikki)
Andrew Cattermole wrote:
> ...
My main critiques:
1. This is a structural approach to discriminated unions that is
a poor fit for D's (mostly) nominal type system. Specifically, I
think the implicit widening is a bad idea because it could lead
to confusion when mixing sumtypes from completely different
domains which just happen to be implicitly convertible via said
widening.
2. The justification of being able to chain matches and
interleave them with range code is not compelling enough to make
the unfamiliar match syntax worthwhile in my mind (some examples
of how this will improve existing code or new patterns it would
enable would help).
3. Also, it makes built in language syntax look like user-written
delegates, which will be confusing.
4. Match arms being allowed to return separate types and silently
evaluating to a newly synthesized sumtype is just asking for
trouble. It's going to be a source of a lot of frustration and
compiler errors, and potentially even bugs. What other language
that has sum types does this?
5. "No static foreach and so on support for the match handlers."
For me, this is a hard requirement for any such proposal. Maybe
others will disagree.
6. "The library form remains fully supported and continues to
serve code that needs template-computed variant sets or cannot
migrate. The language form is additive."
I think the minimum baseline for any successful proposal is that
it can completely replace std.sumtype. Otherwise, what's the
point? FYI the library solution becomes completely redundant with
my proposal:
```d
enum union SumType(Variants...)
{
alias Types = NoDuplicates!Variants;
static foreach (i, V; Variants)
static if (isBuiltInType!T)
case V;
else
mixin($"case $(T.stringof) = T;"); // type
aliasing/punning e.g. case ExternalStruct = ExternalStruct
// Implement the other member functions from std.sumtype
}
```
And Variant/Algebraic/Nullable for that matter.
7. "Catch-all arm: (x) => 42 — matches any variant, parameter
type inferred from variant"
How does this work? In std.sumtype it works because the template
lambda acts as a catch all whose type is inferred, but I don't
think that can work outside of a template function context (?) Is
the type of x a synthesized union containing the types that
weren't matched?
8. "If any unnamed variant has a type whose identifier is None
(e.g., struct None {}), that variant becomes the default. The tag
is initialized to that variant's index, and the corresponding
variant field is initialized to its type's .init."
I don't like this special casing. Why not at least use
typeof(null) instead, which is already D's built in unit type?
(And void, but unlike typeof(null), you can't create a value of
type void). It should be that the .init value of the union is the
init value of its first syntactically declared member. So you
just put your None case first, and that becomes the natural
default.
I think the rest of it looks good. I'm not crazy about the
sumtype syntax, but it's not a deal breaker (though I think the
syntax in my proposal is much better 😉).
More information about the dip.development
mailing list