Second draft: Sum Type by Struct
Meta
jared771 at gmail.com
Tue Sep 15 16:00:34 UTC 2026
On Tuesday, 15 September 2026 at 15:01:46 UTC, Richard (Rikki)
Andrew Cattermole wrote:
>
> On 05/09/2026 6:19 PM, Meta wrote:
>> On Friday, 4 September 2026 at 08:20:42 UTC, Richard (Rikki)
>> Andrew Cattermole wrote:
>>> It supports both by-ref and multi-level matching.
>>>
>>> If we need to expand it we would have the capacity to do so,
>>> although at this stage I don't have a reason to.
>>
>> We need full blown pattern matching, or what's the use of
>> introducing a match statement? There's no point to half
>> measures here:
>
> So we can add it later.
>
> The form accepted is very limited so that it can be expanded
> upon later.
Gotcha, I ended up doing the same with my proposal. I have the
idea for general pattern matching mostly fleshed out in my head,
but haven't written it down yet.
>>> Ocaml
>>>
>>> ```ocaml
>>> type input_type = A | B
>>>
>>> let evaluate_arm (input : input_type) =
>>> match input with
>>> | A -> `Int 42 (* This arm evaluates to a
>>> polymorphic variant containing an int *)
>>> | B -> `String "hello" (* This arm evaluates to a
>>> polymorphic variant containing a string *)
>>>
>>> let () =
>>> let res = evaluate_arm A in
>>> match res with
>>> | `Int n -> Printf.printf "Int: %d\n" n
>>> | `String s -> Printf.printf "String: %s\n" s
>>> ```
>>>
>>>> 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.
>>>
>>> Previous design supported it.
>>>
>>> It would be a huge amount of work, for something that could
>>> be solved with:
>>>
>>> ```d
>>> (v) => () {
>>> ...
>>> }()
>>> ```
>>
>> Ah, I thought you meant that it's not possible to generate
>> match arms with static if.
>>
>>> If it is a problem, then maybe a follow up DIP can do it, but
>>> I don't think the ROI is present for me to do it atm.
>>>
>>>> 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:
>>>
>>> The point of that statement is that it won't go round
>>> breaking code.
>>>
>>> This DIP exceeds what std.sumtype can do.
>>>
>>>> ```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.
>>>
>>> Unfortunately it doesn't solve for result types in general,
>>> it can't.
>>>
>>> Result types require two language integrations, opCast, and
>>> some way to check then unwrap like opUnwrapIfTrue.
>>
>> 1. D already has opCast
>> 2. That's an arbitrary requirement you made up that has
>> nothing to do with Result types.
>
> Right, the person who intends to use this as a key design point
> of PhobosV3, naturally has opinions.
So what happens if your DIP doesn't get accepted? Then you're SOL
for PhobosV3 and have to rethink your design. Result types
*themselves* don't need opCast or opUnwrapIfTrue. I think
whatever design of PhobosV3 you have in mind should be kept
separate from the design of the rest of the language. IMO
Language design should drive library design, not vice-versa.
>> That being said, the if-let pattern in both Rust and Swift
>> accomplishes this in a much simpler way:
>>
>> ```rust
>> Option<int> result = Some(99);
>> if let Some(n) = result {
>> ...
>> } else {
>> ...
>> }
>> ```
>
> Note that this is a variable declaration, just like D has.
>
> What it doesn't have is the integration of implied variable
> declaration.
It does, of a sort - the limited template type pattern matching
syntax it already has. I think it's time to bring some of that
into regular code with pattern matching.
> I'm not convinced that this is the right way to solve it,
> specifically because result types can have a much larger API
> and internal logic than just a sumtype.
Like what? I can't picture what you're thinking of.
>>>> 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 would prefer to use identifier types instead, they don't
>>> have a declaration associated with it. However I don't see a
>>> way to get them approved right now. Previous designs used
>>> them.
>>>
>>> It was part of my member of operator work, but due to their
>>> implicit conversions it was just going to be an uphill battle
>>> with Walter either not understanding it or not liking it.
>>
>> Why do you need that? Just tell the programmer to make the
>> first type of the union its None value, and adopt the rule
>> that the union's init value is that first type's init value.
>> It's such a simple solution rather than having the compiler
>> recognize a special, arbitrary symbol.
> To try and make it consistent with other types default
> initialization.
I'm still not following. Having the .init value of the union
being the tag = 0 (so the first variant is active) and the value
stored being the .init value of the first type in the union,
seems very logical and straightforward to me, in addition to
being right in line with default initialization in the rest of
the language, rather than having to complicate things with a
special rule (which I see as NOT consistent with the rest of the
language).
More information about the dip.development
mailing list