www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Sum type, the D way

reply deadalnix <deadalnix gmail.com> writes:
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
Nov 29 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
My original sumtype design was indeed extending the enum. But eventually, 
trouble surfaced. For example, the pattern matching that goes on with types, 
such as for IsExpressions, and for template parameters. Suddenly, existing code 
that is expecting an enum would match a sumtype, likely causing problems.

Another problem is, as you mentioned, the open nature of enums, which won't
work 
with sumtypes.

Then there's the issue of implicit casting of enums to their basetype. Not sure 
sumtypes are implicitly convertible.

So then it just seemed simpler with a lot fewer special cases to make sumtypes
a 
separate entity. One problem that does resolve, is they'll be closed, which I 
think you'll like :-)
Nov 29 2022
parent reply deadalnix <deadalnix gmail.com> writes:
On Tuesday, 29 November 2022 at 18:58:59 UTC, Walter Bright wrote:
 My original sumtype design was indeed extending the enum. But 
 eventually, trouble surfaced. For example, the pattern matching 
 that goes on with types, such as for IsExpressions, and for 
 template parameters. Suddenly, existing code that is expecting 
 an enum would match a sumtype, likely causing problems.
I'm sure this can be sorted out.
 Another problem is, as you mentioned, the open nature of enums, 
 which won't work with sumtypes.
It already doesn't work today, with things like final switch. You have the power to fix it. This is exactly why I'm telling you to not roll out sum types right now. You are building on top of quick sands.
 Then there's the issue of implicit casting of enums to their 
 basetype. Not sure sumtypes are implicitly convertible.
I don't understand what the issue is here.
 So then it just seemed simpler with a lot fewer special cases 
 to make sumtypes a separate entity. One problem that does 
 resolve, is they'll be closed, which I think you'll like :-)
Making the whole thing a special case doesn't typically translates into fewer special cases.
Nov 29 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 11:40 AM, deadalnix wrote:
 On Tuesday, 29 November 2022 at 18:58:59 UTC, Walter Bright wrote:
 My original sumtype design was indeed extending the enum. But eventually, 
 trouble surfaced. For example, the pattern matching that goes on with types, 
 such as for IsExpressions, and for template parameters. Suddenly, existing 
 code that is expecting an enum would match a sumtype, likely causing problems.
I'm sure this can be sorted out.
I'm a lot less sure. I expect a barrage of corner cases nobody expected. It's also confusing to users - they've come to expect how enums behave, and suddenly it's different. There's also the issue of when is it an enum and when is it an enum subtype. The syntax gives it away, but in my experience, I expect endless confusion. Calling it "sumtype" is also clarifying what it is. It is not some special enum. It's a sumtype, making it clear that D supports sumtypes. Naming things clearly is very important. People already have in their minds what an enum is, and it's not a sumtype. Remember when we decided to use "enum" to declare manifest constants? It made a lot of sense from a programming point of view to do that, but users found it very confusing.
 Another problem is, as you mentioned, the open nature of enums, which won't 
 work with sumtypes.
It already doesn't work today, with things like final switch. You have the power to fix it.
By breaking a lot of existing code, particularly the common use of flags. I would expect a lot of unhappy users.
 This is exactly why I'm telling you to not roll out sum types right now. You
are 
 building on top of quick sands.
Building a new type is not building on quick sand. It's a clean sheet.
 Then there's the issue of implicit casting of enums to their basetype. Not 
 sure sumtypes are implicitly convertible.
I don't understand what the issue is here.
enums are eager to convert to their base type. sumtypes are not, in fact, the base type of a sumtype cannot be determined at compile time.
 So then it just seemed simpler with a lot fewer special cases to make sumtypes 
 a separate entity. One problem that does resolve, is they'll be closed, which 
 I think you'll like :-)
Making the whole thing a special case doesn't typically translates into fewer special cases.
It makes for a much more understandable design, because the two aren't mixed up with if statements. They become separate topics.
Nov 29 2022
parent deadalnix <deadalnix gmail.com> writes:
On Tuesday, 29 November 2022 at 20:17:21 UTC, Walter Bright wrote:
 Remember when we decided to use "enum" to declare manifest 
 constants? It made a lot of sense from a programming point of 
 view to do that, but users found it very confusing.
I always foudn this confusing, but this is what it is.
 By breaking a lot of existing code, particularly the common use 
 of flags. I would expect a lot of unhappy users.
Add an easy way to make these open sets. I even proposed a syntax. The alternative is unsound fundamental and having to pile up more and more new feature instead of extending existing one, because the existing ones are unsound.
 This is exactly why I'm telling you to not roll out sum types 
 right now. You are building on top of quick sands.
Building a new type is not building on quick sand. It's a clean sheet.
No it is not. This new types will have to interact with all the existing types. The complexity of that approach grows quadratically. But it has the benefit of being able to ignore the problem for longer.
 enums are eager to convert to their base type. sumtypes are 
 not, in fact, the base type of a sumtype cannot be determined 
 at compile time.
Well we have opaque enums and non opaque ones already today. While I do agree this is nonsensical, it is nevertheless the current state of affair and whether we make sum type as an enum opaque or not don't make things any worse on this front.
 It makes for a much more understandable design, because the two 
 aren't mixed up with if statements. They become separate topics.
It doesn't. While it makes for the design of sum type itself easier, it create a quadratically growing matrix of interactions. This is a bad tradeof.
Nov 29 2022