Second draft: Sum Type by Struct

Richard (Rikki) Andrew Cattermole richard at cattermole.co.nz
Tue Sep 15 16:42:07 UTC 2026


On 16/09/2026 4:16 AM, Meta wrote:
> On Tuesday, 15 September 2026 at 16:06:22 UTC, Richard (Rikki) Andrew 
> Cattermole wrote:
>>> 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.
>>
>> Then it becomes a problem for Adam to solve.
>>
>> Either I get the problems solved that I can foresee, or its not worth 
>> my limmited energy being put towards a library that I think isn't the 
>> right way to go for D's standard library.
> 
> The problem is that I feel like you're pessimizing the solutions to the 
> current problems that exist *now* in favour of optimizing for solutions 
> to future problems that don't exist yet, and may *never* exist.
> 
>>>> 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.
>>
>> Future/Promise.
>>
>> These are result types.
> 
> Well technically futures/promises and result types are both 
> instantiations of the more general type class of monad, but I get what 
> you mean. I've spent a lot of time working with futures/promises in 
> Java, where they don't have opCast or opUnwrapIfTrue, and it wasn't a 
> bad experience. But I'm curious how those two features would make it 
> better.

Java has checked exceptions, there are interactions to other language 
features allowing the cases I care about to not need special handling as 
much. Also tbf I don't think await needs to evaluate to a value, and 
instead be a yield (which isn't how other languages do it).

And languages like Scala have arbitrary "match" expressions:

```scala
   def anotherFunc[T](response: Future[Either[Throwable, T]]) = {
     response.onComplete {
       case Failure(exception) => // process exception
       case Success(value)     => // process value
     }
   }
```

To me, trying to shoe horn matching into solving this means it is 
overused. I prefer it to be a lot more constrained and explicit.

>>> 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).
>>
>> Default initialization goes a step further, it specifies a lack of a 
>> value. NaN, null these are key examples of it.
>>
>> None is the sumtype equivalent.
> 
> Or again, just use typeof(null), which is that "lack of value" signifier 
> that D already has. None is necessary in languages like Rust that don't 
> have nullable references. In D, I see it as redundant.

I don't care too much if its a struct or that.

If it works, its equivalent.

However that was my explanation for why its special cased for default init.



More information about the dip.development mailing list