Sum type, the D way

deadalnix deadalnix at gmail.com
Tue Nov 29 18:24:37 UTC 2022


While I think  I made it fairly clear that my position is that 
sum types aren't what D should focus on at this time, I have the 
feeling this is going to be ignored, and that we are going to 
roll out something regardless, so I might as well put a few 
things here to make sure we at least end up with something that 
fit within a language and its spirit.

We already have a sum type like feature in D: enum. Wait what? 
Well enum, short for enumerations, are types that specify a set 
of value a variable can take. sum types are just an extension of 
that idea: instead of simply providing a set of values, they also 
allow to provide entire classes of allowable values, types.

So we could simply have:

```d
enum MySumType {
     int,
     bool,
     MyStruct,
     // ...
}
```

D is a system programming language, which means one needs to be 
able to specify how the bits are layed down. Thanksfully, enums 
already provide us with a syntax to provide the backing type for 
our enum, and that won't be enough here. But just like ranges, we 
can duck type the whole thing the following way:

```d
enum MySumType : BackingType {
     int,
     bool,
     MyStruct,
     // ...
}

// We assume that MySumType is able to generate some magic enum 
internally,
// such as MySumType.__Kind is an enum with the different types 
in there.
struct BackingType {
     MySumType.__Kind kind;
     union {
         This,
         That,
         TheOther,
     }

     // opAs is used by the compiler to extract the right union 
member.
     auto opAs(MySumType.__Kind T)() {
         // Return the right element in the union.
     }
}
```

The BackingType can be made 100% optional and compiler generated 
when none is provided.

This is obviously a half assed proposal, as I can already see 
ambiguity in the syntax thinking of it for minutes, but my goal 
here is for people to think a bit about what that feature looks 
like *FOR D*.

Without a coherent design we only get exploding complexity and 
things that don't fit together. We have an exemple of that in D 
currently that is definitively affecting that design (which is 
why we are finding ourselve looking at introducing a new feature 
rather than generalizing an existing one): D is completely 
schizophrenic about whether enum are a closed or an open set. 
`final switch` assumes closed, but binary operators such as `|` 
assume open.

Well, if forces us to answer that question (or to ignore it and 
add to the pile of bear traps) so I will answer it: the set of 
allowable value must be closed, as this is the construct that 
naturally extends towards supporting sum types.

But you'll ask me, we have these open set, what do we do about 
them? We do this:
```d
enum CloseSet { A, B }
enum OpenSet { A, B, ... }
```

Voila, problem solved. When using the first one, operator like 
`|` either fall back to the base type, or plain don't work. When 
using the second one, final switch must, in addition to handled 
all the value, provide a default label.

Voila, now what about pattern matching these thing? Like this: 
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2392r0.pdf


More information about the Digitalmars-d mailing list