Second draft: Sum Type by Struct
Nick Treleaven
nick at geany.org
Sat Sep 5 16:02:08 UTC 2026
On Saturday, 5 September 2026 at 14:54:55 UTC, Richard (Rikki)
Andrew Cattermole wrote:
> On 06/09/2026 2:19 AM, Nick Treleaven wrote:
>> I also think an inferred sumtype result is surprising. What if
>> I want the result type of the match expression to be inferred
>> from the common type of all the results, how would I even
>> write that? I think inferred common type should be the
>> default, which is consistent with the `?` operator.
>
> You would add the casts to each arm.
That can be very verbose, and it's not *inferred*. Suppose the
results are two class instances that inherit from a common base
class. With your proposal, instead of getting a base class result
type, I would get a sum type with the specific class types? I
don't think that is good ergonomics.
>> There should be some way to manually specify the result type
>> (which could be a sum type or not), the compiler is not always
>> going to get the desired type whatever the rules. E.g.
>> `expr.match -> int { ... }`. And it would indeed be nice to
>> allow that result type to be another sum type.
> ```d
> int res = st.match {
> (T v) => "str",
> (U v) => 2
> }; // error
> ```
>
> Sumtypes widen on assignment, so that isn't an issue if you
> want a sumtype.
What if I don't want a sumtype result, and I want to specify the
type in one place? E.g. for my base class scenario.
> The match narrows to a single type, just like a sumtype of one
> variant becomes that variant's type.
>
> It doesn't need more syntax, especially one that I haven't
> spotted in other languages.
I did some research and you're right about other languages. I
thought I had seen it somewhere. One reason some languages don't
need it is (like Rust), they have reverse type inference. So they
use the expected type:
```rs
let r: i8 = match i { // specify i8 as result type
n if n <= 1 => 1,
n => 2,
};
```
>> Also, using a new set of integer promotion rules (however
>> nice) that aren't compatible with the C rules D inherited
>> would need much stronger justification, and I doubt its a good
>> idea. That plus sum type inference makes this DIP more
>> complicated, and those could be added later in a separate DIP,
>> if needed.
>
> I'm not working from C's promotion rules here, they are
> primarily for math expressions which this is not.
>
> The rules I have worked based upon is the sumtype variants
> where multiple integer types are errors for unnamed variants.
> You can't know which variant to construct.
>
> So I want a rule that is neither lossy, nor will trigger this
> error.
>
> And no, flow sensitive typing is not something you "add on
> later". You do it up front or not at all.
That doesn't make sense. As I suggested, a valid design is to
require there to be a common type for all arms, and if not you
have to specify the result type manually, once. This design is
simpler.
>>> TypeScript:
>>>
>>> ```typescript
>>> function evaluateArm(input: "a" | "b"): string | number {
>>> switch (input) {
>>> case "a":
>>> return 42; // This arm evaluates to a number
>>> (int)
>>> case "b":
>>> return "hello"; // This arm evaluates to a string
>>> }
>>> }
>>
>> I don't know TS, but it looks like the `switch` above does not
>> have a result type at all. BTW I am concerned that IIUC the
>> above pattern (which you chose to quote), cannot be written
>> with your proposal, because it doesn't support `return` in a
>> match arm (i.e. returning from `evaluateArm` directly).
>
> I wanted statement arm bodies, it was in previous designs.
>
> But yeash there is nothing in the expression tree that would
> allow it (that I could find, or aware of).
>
> It would be a massive addition.
>
> We could add it later on, since expression just becomes an
> expression statement.
Not being able to use `return` in a match *expression* is
understandable. Not having a match *statement* which can use
`return` seems like a big omission IMO.
> The example is a simple example showing the flow sensitivity of
> matching.
I think you missed my point that the TS example above seems to be
just a switch *statement*, not an *expression*. Chat GPT says:
> TypeScript has only switch statements, not native switch
> expressions.
And also, I'm not convinced that you have given an example of a
switch expression inferring a sum type result type based on its
match arms. The other 2 examples you gave may be using reverse
type inference from the return type of the function. Do you have
another example please? I.e. one with a statement afterwards.
More information about the dip.development
mailing list