First Draft: Nominal Sum Types via `enum union` and `switch` Expressions

Meta jared771 at gmail.com
Tue Sep 15 22:25:13 UTC 2026


On Tuesday, 15 September 2026 at 17:00:14 UTC, Dennis wrote:
> On Tuesday, 15 September 2026 at 15:30:14 UTC, Meta wrote:
>> Compile times and error messages aren't the main reason I 
>> think language-level sum types are useful, (...)
>> 
>> [Layout optimization is] not core rationale. I think the DIP 
>> stands on its own without that section.
>
> If not for for those 2 items, what is the rationale for adding 
> specifically all the described machinery to the language? I 
> don't see anything else in the rationale section.

To me, the main things that language-level sum types offer are 
stack-based @nogc @safe closed polymorphism with better 
ergonomics than a library solution (which includes better error 
messages and implicit construction), and *without* having to use 
templates.

Also, you can't do implicit construction or branch guards with a 
library solution. I've put a lot of thought into how it could be 
done, but anything I've come up with is so ugly that it's not 
worth bothering.

>> But the error messages are significantly better and more 
>> accurate by simple virtue of the fact that the compiler has 
>> access to a lot of information that user code doesn't.
>
> Such as?

Well for one thing, std.variant.match and std.sumtype.match take 
a list of functions/delegates and template lambdas that it uses 
to match again. If you accidentally return the wrong type from 
one of the functions:
```d
alias Value = SumType!(int, string);

void main()
{
     Value v = "asdf";
     v.match!(
         (int x) => x * 2,
         (string s) => s
     );
}
```
```
/dlang/dmd/linux/bin64/../../src/phobos/std/sumtype.d(2020): 
Error: expected return type of `int`, not `string`:
                         return 
mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, 
")");
                         ^
/dlang/dmd/linux/bin64/../../src/phobos/std/sumtype.d(2020):      
   Return type of `int` inferred here.
/dlang/dmd/linux/bin64/../../src/phobos/std/sumtype.d(1662): 
Error: template instance `std.sumtype.matchImpl!(Flag.yes, 
function (int x) pure nothrow @nogc @safe => x * 2, function 
(string s) pure nothrow @nogc @safe => 
s).matchImpl!(SumType!(int, string))` error instantiating
         return matchImpl!(Yes.exhaustive, handlers)(args);
                                                    ^
onlineapp.d(8):        instantiated from here: 
`match!(SumType!(int, string))`
     v.match!(
      ^
```

Whereas with a language solution:
```d
	enum union Value
	{
		case int, string
	}

	Value v = "asdf";
	auto result = switch (v)
	{
		case int x => x * 2,
		case string s => s,
	};
```
```
compiler/test/runnable/testenumunion.d(88): Error: switch 
expression arms must have the same type, not `int` and `string`
         case string s => s,
         ^
```

This isn't a knock against std.sumtype; it's an incredibly 
well-designed implementation. But no matter how good the 
implementation is, it'll never be able to look inside a delegate 
or template lambda passed to `match`, to show the user where 
exactly it's failing; and it'll never be able to offer error 
messages as good as those a language-level solution can. Not 
without AST macros, at least.

But I don't want to frame this as library solution vs. language 
solution. I think that'd end in an unproductive debate between 
two camps that hold differing views on what belongs in the 
language and what belongs in a library. I think the correct 
framing is "does this make code written in D safer, more robust, 
more elegant, more expressive, and does it give programmers more 
power to write their software than they have without it". For me, 
the answer is unquestionably yes.

>> Also doing pretty much anything with a library-based sumtype 
>> involves a *ton* of template instantiations. You don't need 
>> hard numbers to conclude that a language-level implementation 
>> will significantly improve on compile times, memory usage, 
>> symbol bloat, the optimization the compiler can perform, etc.
>
> I understand the real world correlation between template usage 
> and slow compile times is a thing, but I find "library 
> therefore templates, templates therefore slow" a bit weak for 
> such a big DIP. The DIP doesn't dictate anything about the 
> language implementation either. What if it gets implemented by 
> lowering to templates in druntime (like Associative Arrays are 
> now)?
>
>> It's technically true that a library sumtype could do this 
>> type of optimization (although there are still some things the 
>> compiler can do that are impossible for a library type)
>
> Such as?

The compiler can lower a switch expression into a jump table, 
instead of the nested lambda calls of the current library 
solutions. Even if they're aggressively inlined, there's very 
little chance they'll get optimized as well as a language 
construct can be.

On the memory layout side, imagine a `struct Data { ulong a; uint 
b; }`. A library sum type can't reach inside Data to put its 
1-byte discriminant into those 4 unused padding bytes.

>> but it's difficult, tedious, and to my knowledge no library 
>> solution in D has actually done it.
>
> Why would it be easier in the compiler?

Because the compiler has complete knowledge and control of the 
enum union, its contained types, and their respective layouts.

> Btw I'm not speaking as a stickler for std.sumtype who doesn't 
> see the appeal in a language solution. However, I do find the 
> DIP in its current state very lopsided, focused on the "what" 
> and not "why"

Ya, to me the "what" is more important in this case, because 
Walter has already expressed a desire to have sum types in the 
language in some from. Hell, he wrote a DIP for it himself. But 
these are good questions you bring up that I will keep in mind 
for future revisions of the DIP.


More information about the dip.development mailing list