www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.development - Sum Types - first draft

reply Walter Bright <newshound2 digitalmars.com> writes:
https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md


I wrote that some time ago back in November 2022. The idea is to have a
sumtypes 
proposal, followed by a match proposal.

Previous discussions:

https://www.digitalmars.com/d/archives/digitalmars/D/sumtypes_for_D_366242.html

https://www.digitalmars.com/d/archives/digitalmars/D/Sum_type_the_D_way_366389.html

https://www.digitalmars.com/d/archives/digitalmars/D/draft_proposal_for_Sum_Types_for_D_366307.html
Sep 09 2024
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
I'll ignore the formatting issues with the headings.

1. While it is a little like an enum, it is only the tag that is the 
enum, not the elements.
2. Use a hash of the tag name + tag type, this allows it to combine 
between instances by copy alone. This was suggested by Jacob Carlborg 
for my design ages ago and it is brilliant.
3. Sumtypes need to support copy constructors, postblits and 
destructors. Otherwise you limit their usability too significantly. This 
requires a variable size virtual table that goes along with it.
4. Null does not need special casing, either ``typeof(null)`` is in the 
element set or its not.
5. Do not use this ? syntax for determining what the tag type is. That 
will prevent us doing the nullability operators which people including 
myself want. Matching takes over on this, it is not needed.
6. For reading, I would recommend against doing it in `` safe`` code. If 
you want it to be safe, use matching.
7. There is no alternative syntax proposed, there are two different 
syntaxes with different tradeoffs, with one have significantly more 
familiarity to people who have been using sumtypes for 50 years. Both 
need to exist.
8. While it appears to be a good idea to place the tag name using a 
single identifier syntax, it keeps it nice and simple after all, it does 
require that a tagged union has to give each and every element a name. 
Note all existing D implementation of sumtypes do not have this 
requirement, it is too restrictive and does not match the communities 
understanding of the concept. Use my member-of-operator syntax for 
providing this functionality. It was due to be up next... but I'm 
working on escape set.
Sep 09 2024
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 10/09/2024 5:15 PM, Richard (Rikki) Andrew Cattermole wrote:
 I'll ignore the formatting issues with the headings.
 
 1. While it is a little like an enum, it is only the tag that is the 
 enum, not the elements.
 2. Use a hash of the tag name + tag type, this allows it to combine 
 between instances by copy alone. This was suggested by Jacob Carlborg 
 for my design ages ago and it is brilliant.
 3. Sumtypes need to support copy constructors, postblits and 
 destructors. Otherwise you limit their usability too significantly. This 
 requires a variable size virtual table that goes along with it.
 4. Null does not need special casing, either ``typeof(null)`` is in the 
 element set or its not.
 5. Do not use this ? syntax for determining what the tag type is. That 
 will prevent us doing the nullability operators which people including 
 myself want. Matching takes over on this, it is not needed.
 6. For reading, I would recommend against doing it in `` safe`` code. If 
 you want it to be safe, use matching.
 7. There is no alternative syntax proposed, there are two different 
 syntaxes with different tradeoffs, with one have significantly more 
 familiarity to people who have been using sumtypes for 50 years. Both 
 need to exist.
 8. While it appears to be a good idea to place the tag name using a 
 single identifier syntax, it keeps it nice and simple after all, it does 
 require that a tagged union has to give each and every element a name. 
 Note all existing D implementation of sumtypes do not have this 
 requirement, it is too restrictive and does not match the communities 
 understanding of the concept. Use my member-of-operator syntax for 
 providing this functionality. It was due to be up next... but I'm 
 working on escape set.
The reason I have not redone my proposal for sumtypes and posted it here is that I'm waiting on the member-of-operator AND matching syntax. The first matching proposal is in the DIP queue for Walter's and Atila's review. Without both you end up with 4, 5, 6, and 8.
Sep 09 2024
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/9/2024 10:15 PM, Richard (Rikki) Andrew Cattermole wrote:
 I'll ignore the formatting issues with the headings.
Sorry about that. Reading it over again reveals some other problems I'll address shortly.
Sep 10 2024
prev sibling next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 A pattern matching statement suitable for accessing SumTypes is 
 the subject of another DIP.
is it tho? Its the ~~only~~ most important part
Sep 10 2024
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 10/09/2024 10:15 PM, monkyyy wrote:
 On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright wrote:
 A pattern matching statement suitable for accessing SumTypes is the 
 subject of another DIP.
is it tho? Its the ~~only~~ most important part
Certainly important. The best part of this, is matching on types just went for review to W&A!
Sep 10 2024
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md
The evaluation of std.sumtype needs adjustment, because it's full of wrong assumptions.
 - std.sumtype cannot optimize the tag out of existence
It could add that special case if it wanted.
 - cannot produce compile time error if not all the arms are 
 accounted for in a pattern match rather than a thrown exception
It can and does if you use `match` instead of `tryMatch`: ```D SumType!(string, float) s; s.match!((float x) => 1); // std/sumtype.d(2018): Error: static assert: "No matching handler for types `(string)`" ```
 - an int and a pointer cannot both be in a sumtype and be safe
It can and is, that's why DIP1035 added ` system` variables. ```D void main() safe { SumType!(int, int*) s = new int; } ```
Sep 10 2024
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Great! Seems like it has improved substantially since I wrote the proposal a 
couple years ago.

What are its shortcomings presently?
Sep 10 2024
parent reply Dukc <ajieskola gmail.com> writes:
On Tuesday, 10 September 2024 at 19:11:27 UTC, Walter Bright 
wrote:
 Great! Seems like it has improved substantially since I wrote 
 the proposal a couple years ago.

 What are its shortcomings presently?
You're more than smart enough to figure it out yourself, just read the docs. `std.sumtype` is well documented.
Sep 11 2024
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 11 September 2024 at 11:47:47 UTC, Dukc wrote:
 On Tuesday, 10 September 2024 at 19:11:27 UTC, Walter Bright 
 wrote:
 Great! Seems like it has improved substantially since I wrote 
 the proposal a couple years ago.

 What are its shortcomings presently?
You're more than smart enough to figure it out yourself, just read the docs. `std.sumtype` is well documented.
For the record, `std.sumtype` has had no substantial changes made to it since Walter posted his original proposal on November 28th, 2022. This can easily be seen by viewing the git history: https://github.com/dlang/phobos/commits/master/std/sumtype.d
Sep 11 2024
parent Dukc <ajieskola gmail.com> writes:
Paul Backus kirjoitti 11.9.2024 klo 17.34:
 
 For the record, `std.sumtype` has had no substantial changes made to it 
 since Walter posted his original proposal on November 28th, 2022. This 
 can easily be seen by viewing the git history:
 
 https://github.com/dlang/phobos/commits/master/std/sumtype.d
I suspect he might be confusing it with the old `std.variant : Algebraic`. That would explain why he thought your sum type has shortcomings it actually doesn't.
Sep 11 2024
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md
* Special cases are bad. * New capabilities should ideally be general-purpose, not sumtype-specific. * Sumtype syntax should be modeled after unions, not enums.
 * std.sumtype cannot include regular enum members
True, but you can get equivalent semantics using empty structs. For example, this enum: enum Foo : ubyte { a, b; } ...could be translated to this SumType: struct A {} struct B {} alias Foo = SumType!(A, B); Currently, the SumType occupies more storage space than the enum, because it is forced to allocate 1 byte of storage to give the empty struct objects a unique address. If D had a feature like C++'s [[no_unique_address]] attribute [1], these two representations could be made completely identical.
 * std.sumtype cannot optimize the tag out of existence, for 
 example, when having:

       enum Option { None, int* Ptr }
A built-in sum type would not be able to do this either, because in D, every possible sequence of 4 bytes is a potentially-valid int* value. The reason Rust is able to perform this optimization is that Rust has non-nullable reference types [2]. If D had non-nullable pointer types, then std.sumtype could perform the same optimization using reflection and `static if`.
 * cannot produce compile time error if not all the arms are 
 accounted for in a pattern match rather than a thrown exception
 [...]
 * an int and a pointer cannot both be in a sumtype and be safe
Dennis has already addressed these, and his responses are correct.
 Member functions of field declarations are restricted the same 
 way union member functions are.
 [...]
 Members of sumtypes cannot have copy constructors, postblits, 
 or destructors.
std.sumtype does not have these limitations, and having built-in sumtypes limited like this would be a significant step backwards. If you want to start with a proof-of-concept -preview implementation that lacks these features, that's fine--I did the same with the `sumtype` dub package. Support for members with postblits was added in v0.5.0, and support for copy constructors took all the way until v1.0.0. But the DIP should be clear that these limitations will only be temporary.
 A special case of sumtypes will enable use of non-null pointers.
Unprincipled special cases like this are bad language design. Non-null pointers are a generally-useful language feature, even outside of sumtypes. If they're worth doing, they're worth doing properly.
 A new expression, QueryExpression, is introduced to enable 
 querying a sumtype to see if it contains a specified member.
Is this really necessary if we're already planning to add pattern matching?
     SumTypeBody:
        `{` SumTypeMembers `}`

 [...]

     sumtype Option(T) { None, Some(T) }
Using enum-style synatx here is a big mistake, IMO. Sumtypes should use the same AggregateBody syntax as structs and unions. Advantages of AggregateBody: * It's amenable to metaprogramming. Inside an AggregateBody, you can use `static if`, `static foreach`, `mixin`, and so on. With enum-style syntax, your options are greatly reduced. * It would allow sumtypes to have user-defined member functions, including operator overloads. (This is a limitation of std.sumtype that I have personally received several complaints about.) The only disadvantage is that you lose the ability to mix named integer values (like None, above) with typed members (like Some(T)). However, there is a simple solution to this, which is to allow the programmer to declare fields of type `void`: sumtype Option(T) { void none; T some; } This does not have to be a special-case feature of sumtypes; see the abandoned "Give unit type semantics to void" DIP [3] for a detailed description of how this could work as a general language feature.
 The most pragmatic approach for now is to simply disallow 
 taking the address of or a reference to a member of a SumType 
 in  safe code.
This is one valid approach. The other is to make writing to a sumtype value that contains pointers or references system. Keep in mind that merely calling a member function of a struct or class instance requires taking a reference to it, since the `this` parameter is passed by reference. So this limitation is actually quite severe.
 But since a subtype with only enum members can be implemented 
 as an enum, the compiler should do that rewrite. Similarly, a 
 SumType with only one field declaration should be rewritten as 
 a struct (and the tag can be omitted). Furthermore, a subtype 
 with an enum member with a value of 0 and a field declaration 
 that is a pointer can be rewritten as just a pointer.
Again, special cases like this are bad language design--especially in a language like D with powerful reflection and metaprogramming. It's also inconsistent with existing language features. For example, if I declare a type like this: union Example { int n; } ...the compiler does not magically rewrite it as a struct, even though it's functionally equivalent to one. 1. [[no_unique_address]]: https://en.cppreference.com/w/cpp/language/attributes/no_unique_address 2. Non-nullable references: https://doc.rust-lang.org/std/primitive.reference.html 3. Give unit type semantics to void: https://github.com/dkorpel/DIPs/blob/dc1495cc2239729adb270012995c76809fe7f08c/DIPs/DIP1NNN-DK.md
Sep 10 2024
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Thanks for your detailed response. Let me address just one for the moment:

On 9/10/2024 9:20 AM, Paul Backus wrote:
 * std.sumtype cannot optimize the tag out of existence, for example, when
having:

       enum Option { None, int* Ptr }
A built-in sum type would not be able to do this either, because in D, every possible sequence of 4 bytes is a potentially-valid int* value. The reason Rust is able to perform this optimization is that Rust has non-nullable reference types [2]. If D had non-nullable pointer types, then std.sumtype could perform the same optimization using reflection and `static if`.
I was approaching it from the other way around. Isn't a non-nullable pointer a sumtype? Why have both non-nullable types and sumtypes?
Sep 10 2024
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 10 September 2024 at 17:05:49 UTC, Walter Bright 
wrote:
 Thanks for your detailed response. Let me address just one for 
 the moment:

 On 9/10/2024 9:20 AM, Paul Backus wrote:
 * std.sumtype cannot optimize the tag out of existence, for 
 example, when having:

       enum Option { None, int* Ptr }
A built-in sum type would not be able to do this either, because in D, every possible sequence of 4 bytes is a potentially-valid int* value. The reason Rust is able to perform this optimization is that Rust has non-nullable reference types [2]. If D had non-nullable pointer types, then std.sumtype could perform the same optimization using reflection and `static if`.
I was approaching it from the other way around. Isn't a non-nullable pointer a sumtype? Why have both non-nullable types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null). A non-nullable pointer type is a pointer type with its range of valid values restricted. You could think of it as a "difference type"--if you take T*, and _subtract_ typeof(null) from it (i.e., take the set difference [1] of their values), you get a non-nullable pointer type. [1] https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement
Sep 10 2024
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 11/09/2024 5:23 AM, Paul Backus wrote:
 On Tuesday, 10 September 2024 at 17:05:49 UTC, Walter Bright wrote:
 Thanks for your detailed response. Let me address just one for the 
 moment:

 On 9/10/2024 9:20 AM, Paul Backus wrote:
 * std.sumtype cannot optimize the tag out of existence, for example, 
 when having:

       enum Option { None, int* Ptr }
A built-in sum type would not be able to do this either, because in D, every possible sequence of 4 bytes is a potentially-valid int* value. The reason Rust is able to perform this optimization is that Rust has non-nullable reference types [2]. If D had non-nullable pointer types, then std.sumtype could perform the same optimization using reflection and `static if`.
I was approaching it from the other way around. Isn't a non-nullable pointer a sumtype? Why have both non-nullable types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null). A non-nullable pointer type is a pointer type with its range of valid values restricted. You could think of it as a "difference type"--if you take T*, and _subtract_ typeof(null) from it (i.e., take the set difference [1] of their values), you get a non-nullable pointer type. [1] https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement
Yes, Paul is correct. A non-null pointer is guaranteed by the compiler (at compile time), that it may ONLY point to a valid value that is directly usable. A nullable pointer replaces guarantees for UNCERTAINTY. It could point to unmapped memory (i.e. null), junk, or something completely different. The only guarantee is that the pointer itself exists, what it holds is entirely unknown. To resolve this you introduce type state analysis to make guarantees of non-null. Which I have wanted for quite a while now. The amount of uncertainty for pointers in D right now are not good enough if you want to guarantee memory safety. An interesting paper on this subject is [Blame for Null](https://drops.dagstuhl.de/entities/document/10.4230/LIPIcs.ECOOP.2020.3). It reviews a number of languages, and proves using lambda calculus that only nullable pointers can introduce runtime errors (null dereferencing). Of note is that sum types are only discussed as an implementation detail of Scala. " Keeping λnull simple. We could reduce the number of function types and avoid the need for safe applications through a combination of sum types and case analysis. For example, in Scala nullable values are represented with sum types (e.g. a nullable string has type String | Null). The case analysis in turn requires support for flow-typing: "
Sep 10 2024
parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 10 September 2024 at 18:03:32 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 A non-null pointer is guaranteed by the compiler (at compile 
 time), that it may ONLY point to a valid value that is directly 
 usable.
This is actually not true. You are conflating two separate properties: whether a pointer is null, and whether it is valid to dereference. In D, we have safe to guard against _invalid_ pointers, but we do not have any way to guard against null pointers. (Indeed, because of this, safe relies on `null` being _valid_ to dereference!)
Sep 10 2024
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/10/2024 10:23 AM, Paul Backus wrote:
 I was approaching it from the other way around. Isn't a non-nullable pointer a 
 sumtype? Why have both non-nullable types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null).
"the sum of ..." doesn't make it a sumtype?
 A non-nullable pointer type is a pointer type with its range of valid values 
 restricted. You could think of it as a "difference type"--if you take T*, and 
 _subtract_ typeof(null) from it (i.e., take the set difference [1] of their 
 values), you get a non-nullable pointer type.
 
 [1] https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement
How is that different from a sumtype?
Sep 10 2024
parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 10 September 2024 at 19:13:33 UTC, Walter Bright 
wrote:
 On 9/10/2024 10:23 AM, Paul Backus wrote:
 I was approaching it from the other way around. Isn't a 
 non-nullable pointer a sumtype? Why have both non-nullable 
 types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null).
"the sum of ..." doesn't make it a sumtype?
 A non-nullable pointer type is a pointer type with its range 
 of valid values restricted. You could think of it as a 
 "difference type"--if you take T*, and _subtract_ typeof(null) 
 from it (i.e., take the set difference [1] of their values), 
 you get a non-nullable pointer type.
 
 [1] 
 https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement
How is that different from a sumtype?
Let's walk through this very slowly, one step at a time. One way to define a type is as a set of possible values. For example: bool = {false, true} ubyte = {0, 1, 2, ..., 255} Suppose we define a sum type of these two types: Example = bool + ubyte // Equivalent to: // alias Example = SumType!(bool, ubyte); What is the set of possible values for the `Example` type? It's the set that contains all the elements of the `bool` set, and all the elements of the `ubyte` set--in other words, the set union. [1] Example = {false, true} ∪ {0, ..., 255} = {false, true, 0, ..., 255} Now, what are the possible values of a normal, nullable pointer type, like the ones we have in D today? Let's say we're on a 32-bit architecture, just to make the numbers easier to write. In that case: uybte* = {null, 0x00000001, 0x00000002, ..., 0xFFFFFFFF} Each 32-bit integer corresponds to a distinct pointer value, with 0 corresponding to `null`. Now, let's say we add non-nullable pointers to D, so that for every pointer type `T*`, there's a new type `nonnull(T*)` which is just like `T*`, except that it can't be null. What's the set of values for `nonnull(ubyte*)`? nonnull(ubyte*) = {0x00000001, 0x00000002, ..., 0xFFFFFFFF} Naturally, it's the set of values for `ubyte*` with the value `null` removed. In set theory terms, we could write it as the difference between the `ubyte*` set, and the set that contains the single value `null`: nonnull(ubyte*) = ubyte* - {null} As it turns out, there is actually a type in D that corresponds to the set `{null}`--it's called `typeof(null)`: typeof(null) = {null} So, by substitution, we can write: nonnull(ubyte*) = ubyte* - typeof(null) Or, in English: the type `nonnull(ubyte*)` is the _difference_ between the types `ubyte*` and `typeof(null)`. Finally, what happens if we create a sum type of `nonnull(ubyte*)` and `typeof(null)`? Sum = nonnull(ubyte*) + typeof(null) Sum = {0x00000001, ..., 0xFFFFFFFF} ∪ {null} Sum = {null, 0x00000001, ..., 0xFFFFFFFF} Wait a minute...we've seen that set before! It's the set for `ubyte*`! So, once again, by substitution, we can write: Sum = ubyte* ubyte* = nonnull(ubyte*) + typeof(null) Or, in English: the type `ubyte*` is the _sum_ of the types `nonnull(ubyte*)` and `typeof(null)`. [1] https://en.wikipedia.org/wiki/Union_(set_theory)
Sep 10 2024
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/10/2024 10:23 AM, Paul Backus wrote:
 I was approaching it from the other way around. Isn't a non-nullable pointer a 
 sumtype? Why have both non-nullable types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null).
"is the sum of..." makes it a sum type, doesn't it?
Sep 10 2024
parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 10 September 2024 at 22:57:05 UTC, Walter Bright 
wrote:
 On 9/10/2024 10:23 AM, Paul Backus wrote:
 I was approaching it from the other way around. Isn't a 
 non-nullable pointer a sumtype? Why have both non-nullable 
 types and sumtypes?
You have it exactly backwards. A _nullable_ pointer type is the sum of a non-nullable pointer type and typeof(null).
"is the sum of..." makes it a sum type, doesn't it?
Let's compare and contrast your statement and my statement side by side: Yours: Isn't a non-nullable pointer a sum type? Mine: A nullable pointer is a sum type. Do you see the difference now? I'm happy to explain anything that's unclear, but at this point, I'm beginning to suspect that you are only skimming my messages, not reading them all the way through.
Sep 10 2024
prev sibling next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 10 September 2024 at 16:20:55 UTC, Paul Backus wrote:
 On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
 wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md
* Special cases are bad. * New capabilities should ideally be general-purpose, not sumtype-specific. * Sumtype syntax should be modeled after unions, not enums. [snip]
Given your involvement with sumtype, it may be useful to provide specific recommendations about what enhancements to the language would be valuable.
Sep 10 2024
prev sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 10 September 2024 at 16:20:55 UTC, Paul Backus wrote:
 On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright

 * std.sumtype cannot optimize the tag out of existence, for 
 example, when having:

       enum Option { None, int* Ptr }
A built-in sum type would not be able to do this either, because in D, every possible sequence of 4 bytes is a potentially-valid int* value.
Isnt the `int*=cast()0` labled as invalid by the type theory inherited from c? You shouldnt do such a thing if you were designing from scratch but I think walters correct here, you *could* even if you shouldnt Likewise you could collapse nullable!float into a wrapper of float and use nan as a invalid state; you shouldnt and nan should be destroyed; but you could.
Sep 10 2024
prev sibling next sibling parent Dukc <ajieskola gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md


 I wrote that some time ago back in November 2022. The idea is 
 to have a sumtypes proposal, followed by a match proposal.

 Previous discussions:

 https://www.digitalmars.com/d/archives/digitalmars/D/sumtypes_for_D_366242.html

 https://www.digitalmars.com/d/archives/digitalmars/D/Sum_type_the_D_way_366389.html

 https://www.digitalmars.com/d/archives/digitalmars/D/draft_proposal_for_Sum_Types_for_D_366307.html
Reviewing without having looked at other replies first. Please study `std.sumtype` a bit more. You list many shortcomings that aren't actually there. It very much [can](https://dlang.org/phobos/std_sumtype.html#.match) provide a compile-time error if not all arms are accounted for. It is safe to use with an int and a pointer. And it can provide regular enum members, albeit in [a bit roundabout way](https://forum.dlang.org/post/dgghljgygysqvbrdpwsi forum.dlang.org). Also, everything I wrote [here](https://forum.dlang.org/post/gtzhxulfcjsrmkbxgsql forum.dlang.org) still applies. Pasting here for reference.
 We don't want this special case for pointers - or at least it 
 needs to be much, much more refined before it carries it's 
 weight. If I have sumtype S { a, int* b }, S.a == S.b(null);, 
 right? Well, why doesn't the DIP say the same should happen 
 with sumtype S { a, Object b } ? Even more interesting case, 
 sumtype S { a, b, c, d, bool e}. A boolean has 254 illegal bit 
 patterns - shouldn't they be used for the tag in this case? And 
 what happens with sumtype S {a, int* b, int* c}? Since we need 
 space for a separate tag anyway, does it make sense for null b 
 to be equal to a?

 The proposed special case doesn't help much. If one wants a 
 pointer and a special null value, one can simply use a pointer. 
 On the other hand, one might want a pointer AND a separate tag 
 value. To accomplish that, the user will have to either put the 
 0 value to the end or do something like sumtype S {int[0] a, 
 int* b}. Certainly doable, but it's a special case with no good 
 reason.

 The query expression is not a good idea. This introduces new 
 syntax that isn't consistent with rest of the langauge. 
 Instead, I propose that each sumtype has a member function has, 
 that returns a DRuntime-defined nested struct with an 
 opDispatch defined for quessing the tag:
 ```
 sumtype Sum {int a, float b, dchar c}

 auto sum = Sum.b(2.5);

 assert(!sum.has.a);
 assert(sum.has.b);
 assert(!sum.has.c);
 ```
 Alternatively, we can settle for simply providing a way for the 
 user to get the tag of the sumtype. Then he can use that tag as 
 he'd use it in case of a regular enum. In fact we will want to 
 provide tag access in any case, because the sum type is 
 otherwise too hard to use in switch statements.
Sep 11 2024
prev sibling next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md
Thanks! You should standardize on using either of "sumtypes" or "sum type" but not both. According to https://en.wikipedia.org/wiki/Algebraic_data_type the latter seems preferable.
Sep 11 2024
prev sibling next sibling parent IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md


 I wrote that some time ago back in November 2022. The idea is 
 to have a sumtypes proposal, followed by a match proposal.
Really not keen on this version of sum types. First of all, the comma-separated syntax of enum declarations sucks. You can’t use any conditional compilation, and there’s no room for allowing member functions or constructors. Look at how Swift does this better. It’s important to get a feature like this right, or else we’ll have to live with the consequences of yet another dud language feature. For instance, many people agree that enums were a dud feature, and that they should’ve just been proper sum types like in Swift. Therefore, this DIP repeating the mistakes of D’s enum is unacceptable. Second, the query operator is a waste of a token. The new operator isn’t applicable to other language features, making it feel like an ugly special case. I think we’d be much better off with Rikki’s version of sum types, as they do not possess the aforementioned deficiencies, and have several other improvements over this DIP’s version of sum types.
Sep 15 2024
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 10 September 2024 at 04:06:16 UTC, Walter Bright 
wrote:
 https://github.com/WalterBright/documents/blob/96bca2f9f3520cf53ed5c4dec8e5e2d855e64e66/sumtype.md


 I wrote that some time ago back in November 2022. The idea is 
 to have a sumtypes proposal, followed by a match proposal.

 Previous discussions:

 https://www.digitalmars.com/d/archives/digitalmars/D/sumtypes_for_D_366242.html

 https://www.digitalmars.com/d/archives/digitalmars/D/Sum_type_the_D_way_366389.html

 https://www.digitalmars.com/d/archives/digitalmars/D/draft_proposal_for_Sum_Types_for_D_366307.html
Is there a reason you mention Boost’s `variant`, but not not C++17’s `std::variant`?
Sep 19 2024