www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - draft proposal for Sum Types for D

reply Walter Bright <newshound2 digitalmars.com> writes:
Go ahead, Make My Day! Destroy!

https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Nov 28 2022
next sibling parent reply Daniel N <no public.email> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Cool, what about 'switch' support?
Nov 28 2022
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/28/2022 11:18 PM, Daniel N wrote:
 Cool, what about 'switch' support?
I think you mean a match statement. Another DIP for that.
Nov 29 2022
prev sibling parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 29 November 2022 at 07:18:27 UTC, Daniel N wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Cool, what about 'switch' support?
+1, `switch` or `match` support seems missing, maybe there is a pattern matching DIP in the work?
Nov 29 2022
prev sibling next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I have the feeling that there are several cases where you use `enum thing {...}` instead of `sumtype thing {...}`, this creates a bit of confusion.
Nov 28 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/28/2022 11:30 PM, Basile B. wrote:
 I have the feeling that there are several cases where you use `enum thing
{...}` 
 instead of `sumtype thing {...}`, this creates a bit of confusion.
I would never make a mistake like that!
Nov 29 2022
prev sibling next sibling parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:

This is very nice!

Few comments:

This looks like a typo making recursive grammar:
```d
SumTypeMembers:
     SumTypeMembers     // should it be SumTypeMember?
     SumTypeMember `,`
     SumTypeMember `,` SumTypeMembers
```

 `?x.allow ? x.allow : Xyzzy.busy`
Can this be shortened to `?x.allow : Xyzzy.busy`?
 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).
How will `?x.allow` be rewritten in these cases? Other questions: How will this work with Enum Type Inference? Can this be used as an implementation of optional built-in type in the future? (IMO this should be another DIP but would like to get your thoughts)
Nov 29 2022
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
Few other things that I think should be clarified:

Will sumtype support UDAs for its members?
How will __traits work for sumtype (especially `allMembers` and 
`getMember`)?
Nov 29 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 09:40, Andrey Zherikov wrote:
 Few other things that I think should be clarified:
 
 Will sumtype support UDAs for its members?
It should, just like for `enum` and `union`.
 How will __traits work for sumtype (especially `allMembers` and 
 `getMember`)?
I guess it should work just like for `enum` and `union`.
Nov 29 2022
prev sibling next sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I guess 'void' will continue to be an abominable special case? ``` sumtype Result(T) { error; result(T); } Result(T) bar(alias foo)() { alias R = Result!(typeof(foo())); return whatever ? R.result(foo()) : R.error; } int baz() { return 0; } void qux() {} bar!baz(); // fine bar!qux(); // no way? ```
Nov 29 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 12:52 AM, Max Samukha wrote:
 I guess 'void' will continue to be an abominable special case?
That is a separate topic.
Nov 29 2022
prev sibling next sibling parent reply Dark Hole <dark.hole1 yandex.ru> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don't understand `read` code: ```d Xyzzy x = Xyzzy.busy; Xyzzy y = x.busy; // ok y = x.allow; // runtime error y = ?x.allow ? x.allow : Xyzzy.busy; x = Xyzzy.allow(3); y = x.allow; // ok ``` As I thought the type of `x.allow` is `int` and `x.busy` is (probably) `void`. But then we assigning them to `y` which type is `Xyzzy`. How should it work? Implicit cast? Or it should be read like `y = ?x.allow ? x : assert(0)`? Also what will be in `nothrow` code? Can we access members of sumtype?
Nov 29 2022
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 1:28 AM, Dark Hole wrote:
 ```d
 Xyzzy x = Xyzzy.busy;
 Xyzzy y = x.busy; // ok
 y = x.allow;      // runtime error
 y = ?x.allow ? x.allow : Xyzzy.busy;
 x = Xyzzy.allow(3);
 y = x.allow;      // ok
 ```
 
 As I thought the type of `x.allow` is `int` and `x.busy` is (probably) `void`. 
 But then we assigning them to `y` which type is `Xyzzy`. How should it work?
An implicit cast.
 Implicit cast? Or it should be read like `y = ?x.allow ? x : assert(0)`?
 
 Also what will be in `nothrow` code? Can we access members of sumtype?
Exceptions have nothing to do with sumtypes. The runtime error will be like a buffer overflow error.
Nov 29 2022
prev sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 29 November 2022 at 13:21:25 UTC, rikki cattermole 
wrote:
 "sumtype Option(T) = None | Some(T);"

 Supporting ML-ish style syntax is a must. It gives us one 
 liners and it helps with on boarding those with familiarity 
 with the source material. My bet is a lot of people will prefer 
 it when they are not worried about documenting each member.
I disagree, that syntax with `|` is inconsistent with every other constructs, no need to create mental gymnastic ```D sumtype T { } ``` works like ```D struct T { } ``` works like ```D enum T { } ``` works like ```D class T { } ``` works like ```D interface T { } ``` consistency is key
 Consider:
 
 ```d
 SumType!(int, long, float, double) first() {
 	return second();
 }
 
 SumType!(int, float) second() {
 	return typeof(return).init;
 }
 ```
Why consider? to me this looks like a mistake to allow this About the ? query thing, why not at the end? I'm pretty excited to finally see a proper DIP for tagged union, this feature will greatly improve my code base, specially with the way to currently designed my event system!
Nov 29 2022
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 30/11/2022 7:52 AM, ryuukk_ wrote:
 On Tuesday, 29 November 2022 at 13:21:25 UTC, rikki cattermole wrote:
 "sumtype Option(T) = None | Some(T);"

 Supporting ML-ish style syntax is a must. It gives us one liners and 
 it helps with on boarding those with familiarity with the source 
 material. My bet is a lot of people will prefer it when they are not 
 worried about documenting each member.
I disagree, that syntax with `|` is inconsistent with every other constructs, no need to create mental gymnastic
Only within D. That is the syntax (more or less) of the ML family where this is basically from (within context). But it isn't as inconsistent as you might think even within D. ```d enum Foo(alias v) = v; alias Foo(T) = T; sumtype Foo(T) = None | T; ``` Note this would be in addition to the long form since it'll be missing features, but I fully expect a lot of sum types would be defined using this syntax because those extra features just wouldn't be needed for most uses.
 Consider:

 ```d
 SumType!(int, long, float, double) first() {
     return second();
 }

 SumType!(int, float) second() {
     return typeof(return).init;
 }
 ```
Why consider? to me this looks like a mistake to allow this
Sum types entries are a set, the first function return sum type is a superset of the second one. Why shouldn't it work? It makes perfect sense! Everything being returned is in it. Nothing is being left out, it matches!
Nov 29 2022
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 29 November 2022 at 19:00:23 UTC, rikki cattermole 
wrote:
 On 30/11/2022 7:52 AM, ryuukk_ wrote:
 On Tuesday, 29 November 2022 at 13:21:25 UTC, rikki cattermole 
 wrote:
 "sumtype Option(T) = None | Some(T);"

 Supporting ML-ish style syntax is a must. It gives us one 
 liners and it helps with on boarding those with familiarity 
 with the source material. My bet is a lot of people will 
 prefer it when they are not worried about documenting each 
 member.
I disagree, that syntax with `|` is inconsistent with every other constructs, no need to create mental gymnastic
Only within D. That is the syntax (more or less) of the ML family where this is basically from (within context). But it isn't as inconsistent as you might think even within D. ```d enum Foo(alias v) = v; alias Foo(T) = T; sumtype Foo(T) = None | T; ``` Note this would be in addition to the long form since it'll be missing features, but I fully expect a lot of sum types would be defined using this syntax because those extra features just wouldn't be needed for most uses.
Oh, you are indeed right
 Consider:

 ```d
 SumType!(int, long, float, double) first() {
     return second();
 }

 SumType!(int, float) second() {
     return typeof(return).init;
 }
 ```
Why consider? to me this looks like a mistake to allow this
Sum types entries are a set, the first function return sum type is a superset of the second one. Why shouldn't it work? It makes perfect sense! Everything being returned is in it. Nothing is being left out, it matches!
I had to preprocess it, i didn't notice `return`, i thought it would be a `.init` or `SumType!(int, float)`, it being a template made me confused, even then i'm still uncertain if i grasp it, i can only read simple code lol
Nov 29 2022
prev sibling next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 29 November 2022 at 18:52:58 UTC, ryuukk_ wrote:
 ```D
 sumtype T {
 }
 ```
 consistency is key
Agreed.
Nov 29 2022
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 10:52 AM, ryuukk_ wrote:
 About the ? query thing, why not at the end?
Because then it would make parsing ?: more difficult.
Dec 01 2022
prev sibling next sibling parent reply Paulo Pinto <pjmlp progtools.org> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
What if I game the compiler changing the declaration order? sumtype Pointer { int* Ptr, Null, } or if I have multiple pointer based cases, sumtype Pointer { int* Ptr, Null, int* AnotherPtr, } or only the pointers, sumtype Pointer { int* Ptr, int* AnotherPtr, } From my point of view this should at very least be a compiler error in safe, or the compiler needs to be smart enough to ensure there is at least a non-pointer case and it must be the zero index one.
Nov 29 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 1:37 AM, Paulo Pinto wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
What if I game the compiler changing the declaration order? sumtype Pointer {     int* Ptr,     Null, }
That shouldn't make any difference.
 or if I have multiple pointer based cases,
For multiple non-null pointers: sumtype Pointer { Null, int* Ptr } sumtype TwoPointers { Pointer Ptr1, Pointer Ptr2 }
Dec 01 2022
prev sibling next sibling parent XavierAP <n3minis-git yahoo.es> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Minor typo? «Member functions of field declarations...» Do you mean «Member functions ___or___ field declarations»?
Nov 29 2022
prev sibling next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
One of the motivations for std.sumtype was that it was an alternative to std.variant that has full attribute correctness ( nogc/ safe/nothrow/pure) and betterC compatibility (as well as handling const/immutable). It would be good to mention whether these are also design goals of the DIP. For that matter, std.variant (or std.typecons.Nullable) or other somewhat similar D approaches ([1], [2], [3]) aren't mentioned as part of the prior work. I don't think the DIP has to be a replacement for all these things, but it can't hurt to look over these other projects and see if there is anything in them that would be useful to incorporate into the DIP. [1] https://github.com/libmir/mir-core/blob/master/source/mir/algebraic.d [2] https://code.dlang.org/packages/taggedalgebraic [3] https://code.dlang.org/packages/expected
Nov 29 2022
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
The query expression is kind of similar to safe navigation operators [1], but returns true/false instead of the value. I like it, but I wonder if it is something that might be useful more generally. [1] https://en.wikipedia.org/wiki/Safe_navigation_operator
Nov 29 2022
parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 29 November 2022 at 13:39:19 UTC, jmh530 wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
The query expression is kind of similar to safe navigation operators [1], but returns true/false instead of the value. I like it, but I wonder if it is something that might be useful more generally. [1] https://en.wikipedia.org/wiki/Safe_navigation_operator
Thinking on this a little more...reading a member that is not the value that is in it results in a runtime error. From this perspective, the query expression could be used more generally to detect if something would be an error and to return true or false instead. So something like below ```d import std.stdio: writeln; void main() { int* x = null; writeln(*x); //kills program writeln(?*x ? *x : "error"); } ``` Maybe it makes more sense to just set ?null equal to false and true for any basic types (with non-null values) with some similar, consistent rule for aggregate types. This would avoid special casing behavior for sumtypes. I suppose in the back of my head, I am thinking about a situation where the user has a sumtype in their code and wants to change it to a struct (maybe they realize they only are using one of the members). The query expression would no longer work with the struct so they would need to go through their code to remove it. They can't just change the sumtype to a struct.
Nov 29 2022
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
I accidentally posted this under another (which if you're a moderator, 
feel free to delete that one please).



"Which one is existing is specified by a hidden field called a tag."

Actually I recommend against hiding the field. Make it able to be the 
first member of a struct, that way the struct can be effectively zero sized.



"Members of a sum type can either have integer values assigned 
sequentially by the implementation, unique integer values assigned by 
the programmer, or be values of a specified type."

I know it seems like its a good idea, but this was a key idea that Jacob 
came up with for value type exceptions, use a hash instead. Although 
allowing custom values is a good idea.

Consider:

```d
SumType!(int, long, float, double) first() {
     return second();
}

SumType!(int, float) second() {
     return typeof(return).init;
}
```

In this example you have to inspect and convert every entry manually to 
the other returned sumtype even though it was a superset. There is no 
reason in the theory that this shouldn't work and it does look like it 
should.



"Members of sumtypes cannot have copy constructors, postblits, or 
destructors."

Kills reference counting, can't use it. Can't use it for value typed 
exceptions as the underlying sum type implementation. Not good enough. 
Note when these are not defined you can optimize the copying to be just 
mov's.



"sumtype Option(T) = None | Some(T);"

Supporting ML-ish style syntax is a must. It gives us one liners and it 
helps with on boarding those with familiarity with the source material. 
My bet is a lot of people will prefer it when they are not worried about 
documenting each member.



"Sumtypes do not implicitly convert to any other types. Nor can they be 
cast."

They must cast implicitly to supersets. See above.



"Special Case for Non-Null Pointers"

Okay but how do we disallow null pointers? `` disable`` the Null member 
perhaps?



Finally there needs to be a way to remove a member of a sumtype i.e.

```d
SumType!(int) first() {
     return second().removeMember!int;
}

SumType!(int, float) second() {
     return typeof(return).init;
}
```

This is equivalent to the try/catch statement in a value type exception 
as far as the sumtype is concerned.



I also had more time to think after posting the above:

None is a special value in both ML and type theory literature from what 
I've seen. I recommend not defining it inside of the sum type itself, 
but instead make it ``enum None = void.init;`` this would also help 
cleanup meta-programming code allowing us to remove special cases for void.



Good start though!

https://github.com/rikkimax/DIPs/blob/value_type_exceptions/DIPs/DIP1xxx-RC.md
Nov 29 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 14:45, rikki cattermole wrote:
 
 "Members of sumtypes cannot have copy constructors, postblits, or 
 destructors."
 
 Kills reference counting, can't use it. Can't use it for value typed 
 exceptions as the underlying sum type implementation. Not good enough. 
 Note when these are not defined you can optimize the copying to be just 
 mov's.
 
Oof. Missed that bit. Yes, that limitation won't fly. This must be supported.
Nov 29 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/29/22 9:56 AM, Timon Gehr wrote:
 On 11/29/22 14:45, rikki cattermole wrote:
 "Members of sumtypes cannot have copy constructors, postblits, or 
 destructors."

 Kills reference counting, can't use it. Can't use it for value typed 
 exceptions as the underlying sum type implementation. Not good enough. 
 Note when these are not defined you can optimize the copying to be 
 just mov's.
Oof. Missed that bit. Yes, that limitation won't fly. This must be supported.
Yeah, I wanted to bring this up too. This has to work, and is probably the trickiest complication that libraries like std.sumtype solve. A question here: is this intended to obsolete or trivialize the std.sumtype library? -Steve
Nov 29 2022
prev sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
Something else I want to talk a bit more on.

Sumtypes are not distinct in the type system. They are like alias, its 
just a collection of values/behavior that is given a name.

You would need to use features like templates, conditional compilation, 
UDA's ext. to make it distinct and even then they should be implicitly 
convertible as long as its a superset or equal in members.

This extends over to the AST implementation stuff, it means there would 
need to be a global table containing the actual instances and when you 
instantiate a templated sumtype the instance would effectively be just 
an alias into an entry in that table.

Like None, the non-distinctiveness of sumtypes is fundamental in the 
type theory and comes from set theory. So its important that we get this 
right, because it has a lot of flow on effects.
Nov 29 2022
parent reply oconnor0 <thegreendragon+dlang gmail.com> writes:
On Tuesday, 29 November 2022 at 15:26:07 UTC, rikki cattermole 
wrote:
 Sumtypes are not distinct in the type system. They are like 
 alias, its just a collection of values/behavior that is given a 
 name.

 You would need to use features like templates, conditional 
 compilation, UDA's ext. to make it distinct and even then they 
 should be implicitly convertible as long as its a superset or 
 equal in members.

 This extends over to the AST implementation stuff, it means 
 there would need to be a global table containing the actual 
 instances and when you instantiate a templated sumtype the 
 instance would effectively be just an alias into an entry in 
 that table.

 Like None, the non-distinctiveness of sumtypes is fundamental 
 in the type theory and comes from set theory. So its important 
 that we get this right, because it has a lot of flow on effects.
Can you explain this a little more? Sumtype constructors/tags don't necessarily have to be shared across all possible sumtypes. For example, Ocaml has polymorphic variants that do not tie tags to types (https://v2.ocaml.org/manual/polyvariant.html) and variants that do tie tags to types (https://v2.ocaml.org/manual/coreexamples.html#s:tut-recvariants).
Nov 29 2022
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 30/11/2022 5:18 AM, oconnor0 wrote:
 Can you explain this a little more? Sumtype constructors/tags don't 
 necessarily have to be shared across all possible sumtypes. For example, 
 Ocaml has polymorphic variants that do not tie tags to types 
 (https://v2.ocaml.org/manual/polyvariant.html) and variants that do tie 
 tags to types 
 (https://v2.ocaml.org/manual/coreexamples.html#s:tut-recvariants).
From what I'm seeing its not about the tag, its about how the members get defined and what limit are placed upon the inferation and expansion of sumtypes in usage. We don't have that sort of type system where this is really possible. Other type systems such as the one ML is based on (there are multiple different conflicting branches in the type thoery literature) can utilize such behavior.
Nov 29 2022
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I will be copying my feedback from the other thread in the next few messages (sorry, didn't see this new thread).
Nov 29 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Nice! I think this general design, where it behaves just like a tagged union but safe, makes sense for D. I _really_ wish bad element access resulted in a compile-time error instead of a runtime error though. Basically, you could treat if(?s.member){ } and assert(?s.member); specially, and only allow accesses to s.member if they are guarded by one of them. By default, accessing members is disallowed. The user can then choose between: if(?s.member){ writeln(s.member); } and assert(?s.member); writeln(s.member); To either check the tag manually or opt into the runtime error very explicitly. I think catching errors early during type checking is one of the most compelling things about sum types in other languages, and it would be great if D could get that in some way. The analysis does not have to be particularly sophisticated. In particular, no control flow analysis is required.
Nov 29 2022
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 15:42, Timon Gehr wrote:
 On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
In particular, no control flow analysis is required.
Meant to write "data flow analysis" here. (Should have used the opportunity to edit it x]).
Nov 29 2022
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 6:42 AM, Timon Gehr wrote:
 Nice!
Wow! I expected you to eviscerate it! :-)
 I think this general design, where it behaves just like a tagged union but 
  safe, makes sense for D. I _really_ wish bad element access resulted in a 
 compile-time error instead of a runtime error though.
 
 Basically, you could treat
 
 if(?s.member){
 
 }
 
 and
 
 assert(?s.member);
 
 specially, and only allow accesses to s.member if they are guarded by one of 
 them. By default, accessing members is disallowed. The user can then choose 
 between:
 
 if(?s.member){
      writeln(s.member);
 }
 
 and
 
 assert(?s.member);
 writeln(s.member);
 
 To either check the tag manually or opt into the runtime error very explicitly.
I expect that when a user accesses s.member, he implicitly expects it to be that member. If it isn't, then it's a program bug and hence a fatal runtime error. Adding an assert() in front of it is redundant.
 I think catching errors early during type checking is one of the most
compelling 
 things about sum types in other languages, and it would be great if D could
get 
 that in some way. The analysis does not have to be particularly sophisticated. 
 In particular, no control flow analysis is required.
The compiler could do: writeln(s.a); writeln(s.b); // compile time error with data flow analysis, because if all paths to the second use go through reading s.a, then s.b could not possibly be valid. It would be similar to: if (x == 0) { if (x == 1) deadCode(); } which currently dmd does not do.
Nov 29 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/30/22 04:39, Walter Bright wrote:
 On 11/29/2022 6:42 AM, Timon Gehr wrote:
 Nice!
Wow! I expected you to eviscerate it! :-) ...
Well, it looks useful and would allow me to easily replace some hacky unsafe variant code I have written under time pressure before a deadline. Then it would become memory safe. However, it could be a lot more useful if it also allowed me to check that I got nothing wrong. ;)
 I think this general design, where it behaves just like a tagged union 
 but  safe, makes sense for D. I _really_ wish bad element access 
 resulted in a compile-time error instead of a runtime error though.

 Basically, you could treat

 if(?s.member){

 }

 and

 assert(?s.member);

 specially, and only allow accesses to s.member if they are guarded by 
 one of them. By default, accessing members is disallowed. The user can 
 then choose between:

 if(?s.member){
      writeln(s.member);
 }

 and

 assert(?s.member);
 writeln(s.member);

 To either check the tag manually or opt into the runtime error very 
 explicitly.
I expect that when a user accesses s.member, he implicitly expects it to be that member. If it isn't, then it's a program bug and hence a fatal runtime error.
I don't like fatal runtime errors. I prefer compile time errors.
 Adding an assert() in front of it is redundant.
 ...
It's not redundant if it is a compile-time error by default. The explicit assert is to opt into the runtime error, but normally I'd like the compiler to check correct element access during compile time for my sumtypes. Runtime errors that could have easily been catched at compile time make the feature less useful than it can be. For me, type checking case analysis during compile time is one of the main selling points of sum types.
 
 I think catching errors early during type checking is one of the most 
 compelling things about sum types in other languages, and it would be 
 great if D could get that in some way. The analysis does not have to 
 be particularly sophisticated. In particular, no control flow analysis 
 is required.
The compiler could do:     writeln(s.a);     writeln(s.b); // compile time error with data flow analysis, because if all paths to the second use go through reading s.a, then s.b could not possibly be valid. It would be similar to:     if (x == 0)     {         if (x == 1) deadCode();     } which currently dmd does not do.
I want a compile time error for every wrong access. Maybe it will give compile time errors for accesses that are correct. In those cases I can either rewrite my code to make it easier to spot that it is actually right or I can add an explicit assert to document that the program might crash there in case there is a mistake.
Nov 30 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Some things that are missing: - non-default construction, e.g.: sumtype Result{ error, int value; } auto result1 = Result.error; auto result2 = Result.value(2); The above is a bit unergonomic, maybe there is a better way to expose those constructors. They should exist though, otherwise it is e.g., impossible to initialize an immutable sumtype. - introspection, e.g. is(T==sumtype) and __traits(allMembers, ...) - in particular, it would be nice to provide access to the associated enumeration and the tag, e.g. for Result above, there could be an automatically generated enum like this: enum Result{ error, value, } Then you could do something like: final switch(tag(result)) { case Tag!Result.error: ... case Tag!Result.value: ... } This way, sumtype can be a drop-in replacement for existing unsafe tagged unions. I guess with __traits(allMembers, ...) this can be done in the library, but nothing beats direct access to the tag. - how does it interact with type qualifiers? In particular, I guess you can have a sumtype with only immutable members and reassign them anyway? - pattern matching (though that's acknowledged in the DIP and can probably be designed later)
Nov 29 2022
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 29 November 2022 at 14:45:09 UTC, Timon Gehr wrote:
 On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Some things that are missing: - non-default construction, e.g.: sumtype Result{ error, int value; } auto result1 = Result.error; auto result2 = Result.value(2); The above is a bit unergonomic, maybe there is a better way to expose those constructors. They should exist though, otherwise it is e.g., impossible to initialize an immutable sumtype. [snip]
I was a little confused because the DIP proposal has the same example (though using `Error` instead of `error`) and it didn't have an identifier with it. According to the syntax, a field declaration has to have an identifier with the type. So the only other option is `Error`/`error` is an enum. If that's the case, it's a little confusing because `Error` is a commonly used class already. Am I right that if you wanted to do the same as below ```d import std.sumtype; struct Fahrenheit { double degrees; } struct Celsius { double degrees; } struct Kelvin { double degrees; } alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); ``` then the equivalent with this DIP would be ```d sumtype Temperature { Fahrenheit fahrenheit, Celsius celsius, Kelvin kelvin } ```
Nov 29 2022
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 6:45 AM, Timon Gehr wrote:

 Some things that are missing:
 
 - non-default construction, e.g.:
 
 sumtype Result{
      error,
      int value;
 }
 
 auto result1 = Result.error;
 auto result2 = Result.value(2);

 The above is a bit unergonomic, maybe there is a better way to expose those 
 constructors. They should exist though, otherwise it is e.g., impossible to 
 initialize an immutable sumtype.
Ok
 - introspection, e.g. is(T==sumtype)
Already in the DIP
 and __traits(allMembers, ...)
Ok
 - in particular, it would be nice to provide access to the associated 
 enumeration and the tag, e.g. for Result above, there could be an
automatically 
 generated enum like this:
 
 enum Result{
      error,
      value,
 }
 
 Then you could do something like:
 
 final switch(tag(result)) {
      case Tag!Result.error: ...
      case Tag!Result.value: ...
 }
 This way, sumtype can be a drop-in replacement for existing unsafe tagged 
 unions.
I propose a (future) match statement instead. Sumtypes and match statements are symbiotically joined at the hip. I realize this proposal is somewhat crippled without the match, but I didn't want to put the time into designing the match if sumtypes are DOA.
 - how does it interact with type qualifiers? In particular, I guess you can
have 
 a sumtype with only immutable members and reassign them anyway?
An immutable sumtype could not have its members reassigned.
 - pattern matching (though that's acknowledged in the DIP and can probably be 
 designed later)
Yes.
Nov 29 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/30/22 04:53, Walter Bright wrote:
 
 
 - in particular, it would be nice to provide access to the associated 
 enumeration and the tag, e.g. for Result above, there could be an 
 automatically generated enum like this:

 enum Result{
      error,
      value,
 }

 Then you could do something like:

 final switch(tag(result)) {
      case Tag!Result.error: ...
      case Tag!Result.value: ...
 }
 This way, sumtype can be a drop-in replacement for existing unsafe 
 tagged unions.
I propose a (future) match statement instead.
Why not both? It really should be possible to access the tag.
 Sumtypes and match 
 statements are symbiotically joined at the hip. I realize this proposal 
 is somewhat crippled without the match, but I didn't want to put the 
 time into designing the match if sumtypes are DOA.
 ...
Sure, I understand! However, there has to be a way to extract the tag and statically know which member that tag corresponds to. Otherwise built-in sumtypes don't provide useful functionality that is then only accessible via library types.
 
 - how does it interact with type qualifiers? In particular, I guess 
 you can have a sumtype with only immutable members and reassign them 
 anyway?
An immutable sumtype could not have its members reassigned.
I agree. However, a mutable sumtype with `immutable` members could have its members reassigned.
Nov 30 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md

BTW: I understand extraordinarily well where the desire for supporting 
an explicitly nullable pointer comes from, but I worry that integrating 
it into the more general syntax implicitly is a bit too cute. It will 
lead to bad interactions in generic code.

E.g.:

sumtype WithDefault(T){
     Default;
     T Value;
}

A user does not expect this to behave specially if `T` is a pointer. 
Generic code would have to explicitly check for that case and manually 
undo the rewrite in the library. I don't want to read the resulting 
unwieldy Phobos code.

Explicitly nullable pointers are too good of a feature to just give up 
though, so instead, you could introduce explicit syntax for the null check.

E.g.:

sumtype Nullable{
     Null,
     int* Ptr;
     invariant(Ptr !is null);
}

The semantics of this would be to check the invariant on assignment and 
if it fails, then default-initialize the type instead. There could be 
multiple invariants that each can only refer to one of the members.

Then this would be distinct from:

sumtype DoublyNullable{
     Null,
     int* Ptr;
}

It would also be more general because it allows enforcing more general 
invariants. (But it could also be limited to the special case with null, 
at least in the beginning; what's important is that the different 
behavior is documented explicitly in a difference in syntax.)

Note that with the semantics where a bad member access is a runtime 
error, you don't gain much over just using a raw pointer. Therefore, I 
really think D should statically enforce that the user checks the tag.
Nov 29 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 6:45 AM, Timon Gehr wrote:
 BTW: I understand extraordinarily well where the desire for supporting an 
 explicitly nullable pointer comes from, but I worry that integrating it into
the 
 more general syntax implicitly is a bit too cute. It will lead to bad 
 interactions in generic code.
 
 E.g.:
 
 sumtype WithDefault(T){
      Default;
      T Value;
 }
 
 A user does not expect this to behave specially if `T` is a pointer. Generic 
 code would have to explicitly check for that case and manually undo the
rewrite 
 in the library. I don't want to read the resulting unwieldy Phobos code.
In defense, I would add that Rust appears to just the same thing. Except that Rust pointers can't be null. I do understand the notion that a quirk may cause problems.
 Explicitly nullable pointers are too good of a feature to just give up though, 
 so instead, you could introduce explicit syntax for the null check.
 
 E.g.:
 
 sumtype Nullable{
      Null,
      int* Ptr;
      invariant(Ptr !is null);
 }
I know, but the syntax is a bit unappealing. How about: sumtype Nullable { Null = null, int* Ptr } where the `= null` triggers the special case, as it is not an integer.
 Note that with the semantics where a bad member access is a runtime error, you 
 don't gain much over just using a raw pointer.
What you get is a better error message than seg fault.
 Therefore, I really think D should statically enforce that the user checks the
tag.
The user isn't going to want to do that if he knows the tag is correct. Array buffer overflow checks are also hidden from the user.
Dec 01 2022
next sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 2 December 2022 at 05:23:53 UTC, Walter Bright wrote:
 sumtype Nullable { Null = null, int* Ptr }

 where the `= null` triggers the special case, as it is not an 
 integer.
On Friday, 2 December 2022 at 05:23:53 UTC, Walter Bright wrote:
 I know, but the syntax is a bit unappealing. How about:

 sumtype Nullable { Null = null, int* Ptr }

 where the `= null` triggers the special case, as it is not an 
 integer.
Singling out pointers still feels like a hack. What about all the other types that are already sums with an "invalid" value? sumtype Error { error = 0, int value // error = char.init, char value // error = T.invalid, T value }
Dec 01 2022
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 2 December 2022 at 06:19:42 UTC, Max Samukha wrote:

 sumtype Error
*sumtype Result
Dec 01 2022
parent Andy <andy.pj.hanson gmail.com> writes:
I would suggest implementing option types in the language instead 
of requiring the user to define them using a sumtype. (You might 
lower them to a sumtype though.) First of all because writing 
`T?` is more convenient than writing a sumtype.

If option types are first-class, you could include a syntax for 
unwrapping the option. Compare:

	// someFunc() is an `int?`, x is an `int`
	if (auto x ?= someFunc()) { use(x); }

Vs:

	auto option = someFunc();
	if (?option.value) { use(option.value); }

With the latter syntax I would probably want to define `auto x = 
option.value;` anyway if I were using it multiple times.

You could also use an option as the value for `s.x` where `s` is 
a sumtype and `x` is a member; that way it can't possibly fail 
and there's no need for two separate operations to query and get 
it. Then use `if (auto x ?= s.x)` to access it with no possible 
runtime error. That's better than having to check a boolean 
`?s.x` and then access it later, which is exactly the kind of API 
I would expect from a language without options.
Dec 01 2022
prev sibling next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Friday, 2 December 2022 at 05:23:53 UTC, Walter Bright wrote:
 On 11/29/2022 6:45 AM, Timon Gehr wrote:
 [...]
In defense, I would add that Rust appears to just the same thing. Except that Rust pointers can't be null. I do understand the notion that a quirk may cause problems.
 [...]
I know, but the syntax is a bit unappealing. How about: sumtype Nullable { Null = null, int* Ptr } where the `= null` triggers the special case, as it is not an integer.
 [...]
What you get is a better error message than seg fault.
 [...]
The user isn't going to want to do that if he knows the tag is correct. Array buffer overflow checks are also hidden from the user.
Is it possible to make the syntax `int|float|char` style? We could parameterize it as `alias type(T) = int|float|customType!T;` Or will it mess with bitwise expressions too much? It _kinda_ shouldn't since all the operands in bitwise or are variables, but here they will _all_ be types
Dec 02 2022
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 2 December 2022 at 16:43:45 UTC, Tejas wrote:
 Is it possible to make the syntax `int|float|char` style? We 
 could parameterize it as `alias type(T) = 
 int|float|customType!T;`
Worth noting that templated aliases like this are less useful in practice than they look, due to issue 1807: https://issues.dlang.org/show_bug.cgi?id=1807
Dec 02 2022
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Friday, 2 December 2022 at 16:43:45 UTC, Tejas wrote:
 Is it possible to make the syntax `int|float|char` style? We 
 could parameterize it as `alias type(T) = 
 int|float|customType!T;`

 Or will it mess with bitwise expressions too much? It _kinda_ 
 shouldn't since all the operands in bitwise or are variables, 
 but here they will _all_ be types
It 100% will, because the next step in enlightenment here is to realize that literal are also types with one value, and that the definitively belong in there.
Dec 02 2022
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 03/12/2022 10:28 AM, deadalnix wrote:
 On Friday, 2 December 2022 at 16:43:45 UTC, Tejas wrote:
 Is it possible to make the syntax `int|float|char` style? We could 
 parameterize it as `alias type(T) = int|float|customType!T;`

 Or will it mess with bitwise expressions too much? It _kinda_ 
 shouldn't since all the operands in bitwise or are variables, but here 
 they will _all_ be types
It 100% will, because the next step in enlightenment here is to realize that literal are also types with one value, and that the definitively belong in there.
Yeah its better that we stick to ``sumtype Identifier = Arg1|Arg;`` syntax instead where binary operators don't have to be supported (except inside brackets). Slightly unfortunate since that means we can't define it as part of a return type of a function or as a parameter.
Dec 02 2022
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/2/22 06:23, Walter Bright wrote:
 On 11/29/2022 6:45 AM, Timon Gehr wrote:
 BTW: I understand extraordinarily well where the desire for supporting 
 an explicitly nullable pointer comes from, but I worry that 
 integrating it into the more general syntax implicitly is a bit too 
 cute. It will lead to bad interactions in generic code.

 E.g.:

 sumtype WithDefault(T){
      Default;
      T Value;
 }

 A user does not expect this to behave specially if `T` is a pointer. 
 Generic code would have to explicitly check for that case and manually 
 undo the rewrite in the library. I don't want to read the resulting 
 unwieldy Phobos code.
In defense, I would add that Rust appears to just the same thing. Except that Rust pointers can't be null.
Which makes it not a special case. I would have been on board if the goal was to add non-null pointers.
 I do understand the notion that a quirk may cause problems.
 
It would cause problems in generic code.
 
 Explicitly nullable pointers are too good of a feature to just give up 
 though, so instead, you could introduce explicit syntax for the null 
 check.

 E.g.:

 sumtype Nullable{
      Null,
      int* Ptr;
      invariant(Ptr !is null);
 }
I know, but the syntax is a bit unappealing. How about: sumtype Nullable { Null = null, int* Ptr } where the `= null` triggers the special case, as it is not an integer. ...
Well, as long as it is explicit somehow and by default the behavior is not special it should work.
 Note that with the semantics where a bad member access is a runtime 
 error, you don't gain much over just using a raw pointer.
What you get is a better error message than seg fault. ...
It's a spectrum. ;) compile-time error > runtime error with stack unwinding > runtime error without stack unwinding > segfault
 Therefore, I really think D should statically enforce that the user 
 checks the tag.
The user isn't going to want to do that if he knows the tag is correct.
I am a user and I am absolutely going to prefer having to explicitly check the tag before I access it. It's so much more useful. Compile-time errors are great for refactoring. Any amount of dynamic typing is a hassle.
 Array buffer overflow checks are also hidden from the user.
 
Well, this is not my preference, but I guess the type system is not tracking value ranges across statements. Anyway, additional compiler support is useful even if it does not allow proving every single correctness property.
Dec 02 2022
parent TheGag96 <thegag96 gmail.com> writes:
On Friday, 2 December 2022 at 19:44:07 UTC, Timon Gehr wrote:
 I am a user and I am absolutely going to prefer having to 
 explicitly check the tag before I access it. It's so much more 
 useful. Compile-time errors are great for refactoring. Any 
 amount of dynamic typing is a hassle.
+1. The whole feature seems way less useful without compile-time checking.
Dec 02 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Maybe consider changing the syntax to something like: sumtype ST{ a; int* b; } The reason is that with comma-separated values, metaprogramming is hobbled. I think we really want to be able to do things like: sumtype ST(bool hasC){ a; int* b; static if(hasC){ float c; } } Similar for `static foreach`. The fact that this does not work for `enum`s is among the most annoying limitations of `enum`s.
Nov 29 2022
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 29 November 2022 at 14:46:35 UTC, Timon Gehr wrote:
 On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Maybe consider changing the syntax to something like: sumtype ST{ a; int* b; } The reason is that with comma-separated values, metaprogramming is hobbled. [snip] Similar for `static foreach`. The fact that this does not work for `enum`s is among the most annoying limitations of `enum`s.
I wanted to write something like this in my post, but it really didn’t fit. As for `enum`, can’t semicolon syntax just be added? Can’t we just both allow `enum X { A; B; }` and `enum { A, B }`? (We don’t need to deprecate the comma syntax, it works fine except when it’s a hindrance. It’s not evil.)
Nov 30 2022
parent Kagamin <spam here.lot> writes:
On Wednesday, 30 November 2022 at 14:33:51 UTC, Quirin Schroll 
wrote:
 I wanted to write something like this in my post, but it really 
 didn’t fit. As for `enum`, can’t semicolon syntax just be 
 added? Can’t we just both allow `enum X { A; B; }` and `enum { 
 A, B }`? (We don’t need to deprecate the comma syntax, it works 
 fine except when it’s a hindrance. It’s not evil.)
This. Now we have doc comments and UDAs attached to enum members, it's too much for measly comma syntax.
Dec 05 2022
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Maybe you want to consider this example: struct S(T...){ T members; } I guess this creates multiple cases, but it's a good thing to keep in mind, also for the implementation.
Nov 29 2022
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Another thing to consider: sumtype A{ int a; B b; } sumtype B{ int c; int d; } Is the following allowed? A a; ... if(?a.b.c){ ... }
Nov 29 2022
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Tuesday, 29 November 2022 at 14:51:34 UTC, Timon Gehr wrote:
 On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Another thing to consider: sumtype A{ int a; B b; } sumtype B{ int c; int d; } Is the following allowed? A a; ... if(?a.b.c){ ... }
More importantly does it support? sumtype A{ Null; A a; } If so, are their any plans for default initialization using the GC when it comes to chaining? I.E. a.?a.?a or custom syntax when returning a default value if it said sumtype is determined at run time to have a null type? I.E. int x = a.a ?? 0; without having to type if(?a.a) { int x = a.a; } else { int x = 0; } or using the ternary conditional operator for this? - Alex
Nov 29 2022
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 30/11/2022 4:10 AM, 12345swordy wrote:
 More importantly does it support?
 
 sumtype A{
      Null;
      A a;
 }
At least in the theory that Null is equivalent to None which should be added to our type system and not just defined inline (as it is an existing hole). Question is what to do when you do want to store that typed null.
Nov 29 2022
prev sibling next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
DIP is missing - Canonicalization of `sumtype` members. Specifically: - Independence of order of members `sumtype S {T,U}` should be the same as `sumtype S {U,T}`. - Deduplication of members either as a compiler error or as a silent automatic removal of duplicate types. Moreover, what about `sumtype S {a T, b U}` vs `sumtype S {a U, b T}` and `sumtype S {a T, b U}` vs `sumtype S {b T, a U}`? For reference see how `mir.algebraic.Variant` canonicalizes template type parameters using the trait `TypeSet` at https://github.com/libmir/mir-core/blob/master/source/mir/algebraic.d#L447.
Nov 29 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 16:42, Per Nordlöw wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
DIP is missing - Canonicalization of `sumtype` members. Specifically:   - Independence of order of members     `sumtype S {T,U}` should be the same as `sumtype S {U,T}`.   - Deduplication of members either as a compiler error or as a silent automatic removal of duplicate types.
That does not make much sense to me. You may want multiple distinct cases with the same type.
 Moreover, what about
      `sumtype S {a T, b U}` vs `sumtype S {a U, b T}` and
      `sumtype S {a T, b U}` vs `sumtype S {b T, a U}`?
 
 For reference see how `mir.algebraic.Variant` canonicalizes template 
 type parameters using the trait `TypeSet` at 
 https://github.com/libmir/mir-core/blob/master/source/mir/algebraic.d#L447.
This does not seem to allow naming the cases, also, I think sumtype is supposed to be closer to Algebraic than Variant.
Nov 29 2022
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
So I spent the last working on something that effectively boils down to sum types with a couple of twists. While having this DIP on hand would like have made part of the work easier, it doesn't address the main challenges I ran into (one of them is solved, the other one is still up in the air). The first problem that needed to be solved is automated conversion between sum types members. Think if you are trying to run this operation that exist on A but not on B, and the sum value currently contains a B, then convert that B to an A and then run the operation. The second one is that it ends up replacing builtin types in the codebase by custom types, most notably dynamic arrays (which I'll call slices). Doing so de facto breaks most of the codebase in ways that cannot be easily fixed, because there is no way to have a builtin type that behave like a slice. That second problem is the most important one. As of now, it has occupied roughly half the time I spent on the project as a whole, and it is still not even close to be solved. I was able to develop a custom sum type, which includes custom hash table and clever transform to convert the types into one another, and it's bonkers that this isn't dominating the dev time I have to spend on this.
Nov 29 2022
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 16:54, deadalnix wrote:
 
 The second one is that it ends up replacing builtin types in the 
 codebase by custom types, most notably dynamic arrays (which I'll call 
 slices). Doing so de facto breaks most of the codebase in ways that 
 cannot be easily fixed, because there is no way to have a builtin type 
 that behave like a slice.
How are you replacing slices with sum types precisely? Was it enum tag and a slice, where the tag also determines the length of the slice?
Nov 29 2022
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 7:54 AM, deadalnix wrote:
 The first problem that needed to be solved is automated conversion between sum 
 types members. Think if you are trying to run this operation that exist on A
but 
 not on B, and the sum value currently contains a B, then convert that B to an
A 
 and then run the operation.
Why does the conversion need to be automated? S x; x.A = cast(typeof(S.A))x.B;
 The second one is that it ends up replacing builtin types in the codebase by 
 custom types, most notably dynamic arrays (which I'll call slices). Doing so
de 
 facto breaks most of the codebase in ways that cannot be easily fixed, because 
 there is no way to have a builtin type that behave like a slice.
Can you be more specific about what doesn't work?
Nov 29 2022
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/29/22 1:26 AM, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
In Prior work, I suggest adding [TaggedAlgebraic](https://github.com/s-ludwig/taggedalgebraic) and I also suggest reading about how it works. I find it much nicer to use than std.sumtype. In the DIP, it says that "Member functions of field declarations are restricted the same way union member functions are." What does this mean? I can't find any information on this in the spec. An example of what is not allowed would be helpful. -Steve
Nov 29 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 9:14 AM, Steven Schveighoffer wrote:
 On 11/29/22 1:26 AM, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
In Prior work, I suggest adding [TaggedAlgebraic](https://github.com/s-ludwig/taggedalgebraic) and I also suggest reading about how it works. I find it much nicer to use than std.sumtype. In the DIP, it says that "Member functions of field declarations are restricted the same way union member functions are." What does this mean? I can't find any information on this in the spec. An example of what is not allowed would be helpful. -Steve
I thought it mentioned that copy constructors, postblits, and destructors are not allowed.
Nov 29 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 21:04, Walter Bright wrote:
 On 11/29/2022 9:14 AM, Steven Schveighoffer wrote:
 On 11/29/22 1:26 AM, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
In Prior work, I suggest adding [TaggedAlgebraic](https://github.com/s-ludwig/taggedalgebraic) and I also suggest reading about how it works. I find it much nicer to use than std.sumtype. In the DIP, it says that "Member functions of field declarations are restricted the same way union member functions are." What does this mean? I can't find any information on this in the spec. An example of what is not allowed would be helpful. -Steve
I thought it mentioned that copy constructors, postblits, and destructors are not allowed.
When the tag is managed by the language, you know which of those members to call under which circumstances, so this seems like an arbitrary limitation.
Nov 29 2022
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Nov 29, 2022 at 09:10:56PM +0100, Timon Gehr via Digitalmars-d wrote:
 On 11/29/22 21:04, Walter Bright wrote:
[.[..]
 I thought it mentioned that copy constructors, postblits, and
 destructors are not allowed.
When the tag is managed by the language, you know which of those members to call under which circumstances, so this seems like an arbitrary limitation.
Supporting ctors, postblits, and dtors does increase the complexity of the implementation. Perhaps that can be left as a future extension? In any case, eventually we should support it; otherwise sumtypes will be too limited in their usefulness. T -- Philosophy: how to make a career out of daydreaming.
Nov 29 2022
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 30/11/2022 9:18 AM, H. S. Teoh wrote:
 Supporting ctors, postblits, and dtors does increase the complexity of
 the implementation.
Not by enough to warrant ignoring it. After all, all the logic already exists for structs, just gotta expand upon that and add a little more glue code specializing for sum types with a switch statement.
Nov 29 2022
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 29 November 2022 at 20:18:02 UTC, H. S. Teoh wrote:
 Supporting ctors, postblits, and dtors does increase the 
 complexity of the implementation.  Perhaps that can be left as 
 a future extension?  In any case, eventually we should support 
 it; otherwise sumtypes will be too limited in their usefulness.
Such support is already implemented in std.sumtype, so you can review the code yourself if you would like to see how much complexity it adds to the implementation. Off the top of my head, I can tell you that most of the code volume comes from having to copy and paste each function 4 times to handle different mutability qualifiers (mutable, const, immutable, inout). Which is tedious (and a symptom of a missing language feature IMO), but not exactly complex.
Nov 29 2022
parent deadalnix <deadalnix gmail.com> writes:
On Wednesday, 30 November 2022 at 04:09:10 UTC, Paul Backus wrote:
 Off the top of my head, I can tell you that most of the code 
 volume comes from having to copy and paste each function 4 
 times to handle different mutability qualifiers (mutable, 
 const, immutable, inout). Which is tedious (and a symptom of a 
 missing language feature IMO), but not exactly complex.
It's amazing how many of these these discussion surfaced. This is the actual things that we need to fix. And when they are fixed, if we still feel like the need for builtin sum types remains, then yes, go for it. In the meantime, it is just adding to the pile of things that don't really work.
Nov 30 2022
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/29/22 3:04 PM, Walter Bright wrote:
 On 11/29/2022 9:14 AM, Steven Schveighoffer wrote:
 In the DIP, it says that "Member functions of field declarations are 
 restricted the same way union member functions are." What does this 
 mean? I can't find any information on this in the spec.

 An example of what is not allowed would be helpful.
I thought it mentioned that copy constructors, postblits, and destructors are not allowed.
It mentions postblit and destructors *on the union itself* are not allowed. It says nothing on field declarations. These make sense given that a union itself cannot know what the actual data stored its (unless you embed the type somehow in the data itself). However, a sumtype would not have that problem. Honestly, the trickiest parts of making a library sumtype *are* the lifetime issues. I would hope the builtin thing would solve this for us. -Steve
Nov 29 2022
prev sibling next sibling parent IGotD- <nise nise.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
**1.** Will sumtypes be implemented using lowering which creates struct/union types? if not lowering is used does not follow the member memory layout of struct/unions then it must be defined when it comes to member position, alignment, possible reordering etc. **2.** The "hidden" tag field must be defined and its size and position (is reordering allowed?). Normally it can be represented by an uint8 which usually leaves a gap if it is the first member because of alignment requirements. **3.** One interesting feature with tagged enum in Swift is that they allow for "inheritance". ``` enum BaseErrors { case Success case Error1(String text) case Error2(int val) } enum ExtendedErrors : BaseErrors { case ExtendedError1 case ExtendedError2 } ``` This plays a big role in the error handling in Swift being able to exterd the base Error enum. Is this something that would be interesting for D? **4.** The inline type definitions in Swift are nice like. ``` enum EE { case T1 case T2(String text) case T3(int val) } ``` and you don't have to define the enum types outside the sumtype. What would equivalent in D be? ```d sumtype EE { T1; // Does T1 exist? struct T2 { string text; } struct T2 { int val; } } ``` ??
Nov 29 2022
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 07:26, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I guess another point is that a precise tracing garbage collector can properly understand the sum type memory layout, which is currently impossible with library-defined types
Nov 29 2022
prev sibling next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Regarding ```d sumtype Result { Error, int Value } ``` , is ```d Result res; res = 25; ``` supposed to be supported aswell? If so, why not --- because it requires implicit conversion?
Nov 29 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 12:37 PM, Per Nordlöw wrote:
 ```d
 sumtype Result
 {
      Error,
      int Value
 }
 ```
 
 , is
 
 ```d
 Result res;
 res = 25;
 ```
 
 supposed to be supported aswell?
At the moment, no, should be: res.Value = 25;
 If so, why not
I'd reframe that as why do it? On a pragmatic note, there may be multiple matches for an int in the sumtype.
Nov 30 2022
parent Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Wednesday, 30 November 2022 at 09:33:54 UTC, Walter Bright 
wrote:
 On 11/29/2022 12:37 PM, Per Nordlöw wrote:
 ```d
 sumtype Result
 {
      Error,
      int Value
 }
 ```
 
 , is
 
 ```d
 Result res;
 res = 25;
 ```
 
 supposed to be supported aswell?
At the moment, no, should be: res.Value = 25;
 If so, why not
I'd reframe that as why do it? On a pragmatic note, there may be multiple matches for an int in the sumtype.
I would have preferred a sumtype more similar to a functional language custom type (like in 'Elm'), where every member must be of a different type, just to allow what Per was asking. /Paolo
Nov 30 2022
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I think a section on ABI is missing. It should specify the memory layout of sumtypes, just as for existing language constructs [1]. What's even more important than the memory layout is the value of the tag. Something needs to be specified in regards to the value of the tag. One needs to be able to draw some conclusions about what happens with the value if members are added, removed or reordered. Ideally the tag value of existing members should not change. These are important things when using sumtypes at ABI boundaries. The DIP mention there's a runtime check of the tag, but it doesn't specify what "runtime check" is. It should. Walter did reply to a post that it would be like a buffer overflow error [2]. Which is not entirely clear, but if you overflow a buffer (an array) it throws a RangeError, so I would guess it throws an Error. Yet another language construct that a has a non-obvious dependency on the exception handling system. This is unfortunate. The sumtype is a good alternative for error handling to the existing exception system we have today. If the sumtype is going to depend on the exception system this will be less attractive. A tagged union can be used in any system (embedded, kernel, etc.). If the sumtype is going to depend on the exception system it probably cannot be used in these environments. It would be great if there was no hidden runtime check. Force the user explicitly write code that checks the tag, otherwise it's a compile time error to access a member. Timon already suggested this [3]. This could potentially be extended to allow accessing the field without checking the tag, but require checking the tag before using the value, example: ```d sumtype Result { Error, int Value } Result foo(); void bar() { auto value = foo().Value; // ok, has not been used yet writeln(value); // compile time error to use `value` if (!?value) return; writeln(value); // ok, `value` has been checked above } ``` This would allow early returns and avoid nesting all code in if statements. BTW, `value` now behaves more or less like an optional type. Another breaking change, perhaps an edge case, but code that inspects all types and language constructs and performs different actions, i.e. a serialization library or something like `writeln`. Example: ```d static if (is(T == int)) {} else static if (is(T == enum)) {} // exhaustive checks of all language constructs else static assert(false); ``` Or even worse, if the else statement is missing. The DIP mentions that pattern matching is subject of another DIP. But I think it's reasonable that the switch statement should support sumtypes, in the same way as the if statement. Nothing fancy, just the same feature as the if statement supports. [1] https://dlang.org/spec/abi.html [2] https://forum.dlang.org/post/tm4kbe$23l2$1 digitalmars.com [3] https://forum.dlang.org/post/tm55o9$mq2$3 digitalmars.com -- /Jacob Carlborg
Nov 29 2022
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Nov 29, 2022 at 08:37:57PM +0000, Jacob Carlborg via Digitalmars-d
wrote:
[...]

 
 I think a section on ABI is missing. It should specify the memory
 layout of sumtypes, just as for existing language constructs [1].
Agreed.
 What's even more important than the memory layout is the value of the
 tag. Something needs to be specified in regards to the value of the
 tag. One needs to be able to draw some conclusions about what happens
 with the value if members are added, removed or reordered. Ideally the
 tag value of existing members should not change. These are important
 things when using sumtypes at ABI boundaries.
You can't dynamically change a sumtype (add/remove/reorder its constituents) after the fact, because that may change their size, which implies an ABI change. I think it's pretty much a given that you have to recompile after any change to a sumtype's definition. (Just like you have to recompile when you change a struct's definition -- since its size may no longer be compatible with its old definition.) T -- Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce
Nov 29 2022
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/29/22 21:50, H. S. Teoh wrote:
 What's even more important than the memory layout is the value of the
 tag. Something needs to be specified in regards to the value of the
 tag. One needs to be able to draw some conclusions about what happens
 with the value if members are added, removed or reordered. Ideally the
 tag value of existing members should not change. These are important
 things when using sumtypes at ABI boundaries.
You can't dynamically change a sumtype (add/remove/reorder its constituents) after the fact, because that may change their size, which implies an ABI change. I think it's pretty much a given that you have to recompile after any change to a sumtype's definition. (Just like you have to recompile when you change a struct's definition -- since its size may no longer be compatible with its old definition.)
I guess the point is that in some contrast to structs, it may well be compatible in some cases, and it is important to know in which cases it remains compatible.
Nov 29 2022
prev sibling parent Basile.B <b2.temp gmx.com> writes:
On Tuesday, 29 November 2022 at 20:50:15 UTC, H. S. Teoh wrote:
 On Tue, Nov 29, 2022 at 08:37:57PM +0000, Jacob Carlborg via 
 Digitalmars-d wrote: [...]

 
 I think a section on ABI is missing. It should specify the 
 memory layout of sumtypes, just as for existing language 
 constructs [1].
Agreed.
 What's even more important than the memory layout is the value 
 of the tag. Something needs to be specified in regards to the 
 value of the tag. One needs to be able to draw some 
 conclusions about what happens with the value if members are 
 added, removed or reordered. Ideally the tag value of existing 
 members should not change. These are important things when 
 using sumtypes at ABI boundaries.
You can't dynamically change a sumtype (add/remove/reorder its constituents) after the fact, because that may change their size, which implies an ABI change.
You can add members, Crystal does that all the time.
Nov 30 2022
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/29/2022 12:37 PM, Jacob Carlborg wrote:

Section added.

 
 The DIP mention there's a runtime check of the tag, but it doesn't specify
what 
 "runtime check" is.
Section added.
 This could potentially be extended to allow accessing the field without
checking 
 the tag, but require checking the tag before using the value, example:
Discussed in a reply to Timon.

 
 Another breaking change, perhaps an edge case, but code that inspects all
types 
 and language constructs and performs different actions, i.e. a serialization 
 library or something like `writeln`.
Added

 
 The DIP mentions that pattern matching is subject of another DIP. But I think 
 it's reasonable that the switch statement should support sumtypes, in the same 
 way as the if statement.
A match statement would make this redundant. In fact, I expect the match statement to be lowered to a switch :-)
Nov 29 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
As I expected, the threads on sum types have a lot of posts, and each reply I 
make spawns many more. I apologize if I don't respond in depth to all of them.
Nov 29 2022
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Wednesday, 30 November 2022 at 03:23:42 UTC, Walter Bright 
wrote:
 As I expected, the threads on sum types have a lot of posts, 
 and each reply I make spawns many more. I apologize if I don't 
 respond in depth to all of them.
This phenomenon is what prompted my newest blog post: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_11_28.html#dip-dip,-part-2 I'd formalize some of the feedback stuff to ensure discussions happen before feedback and to remove duplicates.
Nov 30 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/30/2022 6:43 AM, Adam D Ruppe wrote:
 This phenomenon is what prompted my newest blog post:
 http://dpldocs.info/this-week-in-d/Blog.Posted_2022_11_28.html#dip-dip,-part-2
 
 I'd formalize some of the feedback stuff to ensure discussions happen before 
 feedback and to remove duplicates.
Not so easy to do :-/
Dec 01 2022
prev sibling next sibling parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
 cannot produce compile time error if not all the arms are 
 accounted for in a pattern match rather than a thrown exception
It does: ```d SumType!(int, string) s; s.match!((int i) => true); // Error: static assert: "No matching handler for types `(string)`" ```
 `if (?x.busy)`
Please consider moving `?` to be after identifier so the following can be allowed: ```d sumtype C { Default, int value } sumtype B { Default, C c } sumtype A { Default, B b } A a; if(a.b?.c?.value?) ... ``` Compare with `if(?a.b && ?a.b.c && ?a.b.c.value)` This will also allow natural improving of ternary expression: `a.b?:123` === `(a.b?) ? (a.b) : (123)`. (may be just `a.b ? 123`?) Also if you even think about adding optional type, `?`-suffix will perfectly match here: ```d int? optional; if(optional?) writeln("it's set to ", optional); string? s; writeln("the value is ", s?:"not set"); ```
Nov 30 2022
next sibling parent Zealot <no2 no.no> writes:
On Wednesday, 30 November 2022 at 08:49:00 UTC, Andrey Zherikov 
wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 [...]
 [...]
It does: ```d SumType!(int, string) s; s.match!((int i) => true); // Error: static assert: "No matching handler for types `(string)`" ``` [...]
+1 this would actually reduce a lot of clutter.
Nov 30 2022
prev sibling next sibling parent reply Basile.B <b2.temp gmx.com> writes:
On Wednesday, 30 November 2022 at 08:49:00 UTC, Andrey Zherikov 
wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
 cannot produce compile time error if not all the arms are 
 accounted for in a pattern match rather than a thrown exception
It does: ```d SumType!(int, string) s; s.match!((int i) => true); // Error: static assert: "No matching handler for types `(string)`" ```
 `if (?x.busy)`
Please consider moving `?` to be after identifier so the following can be allowed: ```d sumtype C { Default, int value } sumtype B { Default, C c } sumtype A { Default, B b } A a; if(a.b?.c?.value?) ... ``` Compare with `if(?a.b && ?a.b.c && ?a.b.c.value)` This will also allow natural improving of ternary expression: `a.b?:123` === `(a.b?) ? (a.b) : (123)`. (may be just `a.b ? 123`?) Also if you even think about adding optional type, `?`-suffix will perfectly match here: ```d int? optional; if(optional?) writeln("it's set to ", optional); string? s; writeln("the value is ", s?:"not set"); ```
That would not generally work, or that would not be efficient. In the background "optional access" allocates a default value at the use site, meanings an alloca, and then you select the default or the right one if evaluation has succeed. Optional access is more tied to the language. Optional access does not generate good unoptimized code, let's not making its D version worst that what already exists. You see a thing like a?.b = c is like typeof(a.b) fallback; (a.b ? a.b : fallback) = c; where the ternary yields a lvalue. This causes problems with members that will be eventually used in optional accesses. The default value must be related to that particular expression. Using a sum type for optional access is still a hack.
Nov 30 2022
parent reply Basile.B <b2.temp gmx.com> writes:
On Wednesday, 30 November 2022 at 14:22:08 UTC, Basile.B wrote:
 On Wednesday, 30 November 2022 at 08:49:00 UTC, Andrey Zherikov 
 wrote:
 [...]
That would not generally work, or that would not be efficient. In the background "optional access" allocates a default value at the use site, meanings an alloca, and then you select the default or the right one if evaluation has succeed. Optional access is more tied to the language. Optional access does not generate good unoptimized code, let's not making its D version worst that what already exists. You see a thing like a?.b = c is like typeof(a.b) fallback; (a.b ? a.b : fallback) = c; where the ternary yields a lvalue. This causes problems with members that will be eventually used in optional accesses. The default value must be related to that particular expression. Using a sum type for optional access is still a hack.
TLDR; I mean the member of an aggregate is not necessarily a sumtype, but you want optional access on it. Because of that I think that optional access should be a well defined expression in a language. Let's not merge two things because they partially intersect.
Nov 30 2022
parent reply Basile B. <b2.temp gmx.com> writes:
On Wednesday, 30 November 2022 at 14:32:56 UTC, Basile.B wrote:
 On Wednesday, 30 November 2022 at 14:22:08 UTC, Basile.B wrote:
 On Wednesday, 30 November 2022 at 08:49:00 UTC, Andrey 
 Zherikov wrote:
 [...]
That would not generally work, or that would not be efficient. In the background "optional access" allocates a default value at the use site, meanings an alloca, and then you select the default or the right one if evaluation has succeed. Optional access is more tied to the language. Optional access does not generate good unoptimized code, let's not making its D version worst that what already exists. You see a thing like a?.b = c is like typeof(a.b) fallback; (a.b ? a.b : fallback) = c; where the ternary yields a lvalue. This causes problems with members that will be eventually used in optional accesses. The default value must be related to that particular expression. Using a sum type for optional access is still a hack.
TLDR; I mean the member of an aggregate is not necessarily a sumtype, but you want optional access on it. Because of that I think that optional access should be a well defined expression in a language. Let's not merge two things because they partially intersect.
```d // let me explain the problem // you want optional access struct S { int a; } // so you cant get optional access to `a` // because "int" does not allows this so // you write struct S { SomeSumtype a; } // nice but it's not anymore compatible with C // the optional value is part of the struct size // you see the problem ? // // your struct size is not anymore 4 but 8. // // Optional access must be built in the language as an operator. ``` you see what I mean ?
Dec 03 2022
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Dec 03, 2022 at 05:26:41PM +0000, Basile B. via Digitalmars-d wrote:
[...]
 ```d
 // let me explain the problem
 // you want optional access
 
 struct S
 {
     int a;
 }
 
 // so you cant get optional access to `a`
 // because "int" does not allows this so
 // you write
 
 struct S
 {
     SomeSumtype a;
 }
 
 // nice but it's not anymore compatible with C
 // the optional value is part of the struct size
 // you see the problem ?
 //
 // your struct size is not anymore 4 but 8.
 //
 // Optional access must be built in the language as an operator.
 ```
 
 you see what I mean ?
I don't follow. Whether or not the sum type is built into the language, the tag has to be stored *somewhere*, your struct size will not be compatible with C regardless. Just because .a uses a built-in sumtype does not make the tag disappear into thin air, it will still exist in the struct and occupy space, and the result is still not compatible with C because C does not understand sumtypes. Either way, on the C side you have to declare the tag explicitly and deal with it explicitly, regardless of whether sumtypes are built into the language or not. T -- "I'm not childish; I'm just in touch with the child within!" - RL
Dec 03 2022
parent Basile B. <b2.temp gmx.com> writes:
On Saturday, 3 December 2022 at 17:50:19 UTC, H. S. Teoh wrote:
 On Sat, Dec 03, 2022 at 05:26:41PM +0000, Basile B. via 
 Digitalmars-d wrote: [...]
 [...]
I don't follow. Whether or not the sum type is built into the language, the tag has to be stored *somewhere*, your struct size will not be compatible with C regardless. Just because .a uses a built-in sumtype does not make the tag disappear into thin air, it will still exist in the struct and occupy space, and the result is still not compatible with C because C does not understand sumtypes. Either way, on the C side you have to declare the tag explicitly and deal with it explicitly, regardless of whether sumtypes are built into the language or not. T
I dont want `?.` to be related to that, which was proposed at the very beginning of this fork. Since the very beginning of this fork I explain that `ident?.` is something else.
Dec 03 2022
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/30/2022 12:49 AM, Andrey Zherikov wrote:
 Please consider moving `?` to be after identifier so the following can be
allowed:
Problems with: a? and: a?b:c It's not impossible to do, but it requires arbitrary lookahead to resolve and I like to keep the parser simple and fast.
Dec 01 2022
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
1. In *Alternative Syntax* the following is not supported by the grammar you provided: ```d sumtype Option(T) { None, Some(T) } ``` It should probably be ```d sumtype Option(T) { None, T Some } ``` 2. Maybe you should mention anonymous `sumtype` (cf. anonymous `struct` and anonymous `union`). 3. As for a keyword, you could circumvent the problem by re-using existing keywords. `enum union` would be a candidate. 4. “Members of sumtypes cannot have copy constructors, postblits, or destructors.” Limits its application severely. Andrei had a talk explaining how destruction of `std::variant` in C++ could be done efficiently using `static foreach` – if C++ had it. I don’t see how a copy constructor/postblit is evil. 5. “Sumtypes can be copied *if all its constituent types can be copied*.” It seems it tries to be too many things at once and that is confusing. It tries to be: 1. An algebraic union with a tagged union as its special case 2. An optional type 3. Some form of non-nullable annotation. Let’s talk about 3. first: The recognition of a magical pattern is bad; it is backwards and leads to surprises. Were reference types non-nullable from day one, `enum union { typeof(null), int* }` would be a great candidate for a nullable pointer to `int`. The same way `typeof(return)` is special-cased in the grammar (the token `return` not being an expression), you could at least special-case `null` (the keyword) as a possible case of a D sum type. However, this is still a confusing design because it looks like you’re *adding* a case, but actually it *removes* a value form the overall type. I’ve heard of negative types in type theory before, but to be honest, I don’t know much about it. Better than seemingly *adding* `null` would be to add a (visually) negative `null`: Allow the pseudo-member `!null` or `-null`. It’s still weird and maybe even hard to teach, but at least we can tell people: “The same way you can *add* something to 2 to make it 1, namely −1, you can *add* `-typeof(null)` to `int*`, you get an `int*` that cannot be `null`. For your convenience, instead of `-typeof(null)` you can write `-null`.” A sum type consisting of at least one reference type (pointers, classes, AAs) may include `-null` as a special member that makes the nullable options non-nullable. We can even add syntax for simple non-nullable reference types: `int*-null` or `int*\null` or `int*!` or whatever you like. Likely, it’d be a lowering to a template in object.d named `nonnull` or something similar. As for 1. and 2., those make sense. Adding a single, distinct value to a type makes this an optional type. A template `optional` in Phobos would become a vocabulary type and makes people not implement their own optionals and be implemented using a sum type. This could even be added to object.d with syntax. I still think that D would be a better and safer language if it had non-null reference types: In such a hypothetical D, an `int*` always points to an `int`, but an `int*?` might be `null`. For value types, something like `int?` can be done in two ways: Either reserve an otherwise invalid value as the null value (possibly −2³²) or make `int?` an alias for `enum union NullableInt { typeof(null), int }` – the first option is a non-starter because of backward compatibility, but an entirely new language could do that. User-defined types can have a compile-time constant `opNull` that tells the compiler: “Not all combinations of values for my members signify a valid object; use this as the ‘null’ object instead of adding a boolean tag when I’m combined with `?`.” It’s probably worth exploring how much of that can be done and to what cost. Why all this talk about optional types? Because they play well with sum types. Asking for a possible variant of a sum type is inherently returning an optional value. The first step to getting sum types right is getting optional types right. That doesn’t mean optional types cannot be a special case of sum types. In that sense, optional types are the most relevant application of sum types. ```diff EnumUnionDeclaration: `enum` `union` Identifier EnumUnionBody EnumUnionTemplateDeclaration + AnonymousEnumUnionDeclaration + + AnonymousEnumUnionDeclaration: + `enum` `union` EnumUnionBody EnumUnionTemplateDeclaration: `enum` `union` Identifier TemplateParameters Constraint (opt) EnumUnionBody EnumUnionBody: `{` EnumUnionMembers `}` EnumUnionMembers: EnumUnionMember EnumUnionMember `,` EnumUnionMember `,` EnumUnionMembers EnumUnionMember: + 'null' + '!null' EnumMemberAttributes EnumUnionMember EnumMember FieldDeclaration EnumMember: - Identifier - Identifier = AssignExpression + case Identifier + case Identifier = AssignExpression FieldDeclaration: - Type Identifier - Type Identifier = AssignExpression + Type Identifier(opt) + Type Identifier(opt) = AssignExpression QueryExpression: `?` PostfixExpression `.` Identifier ``` I mentioned `null` and `!null` above. `null` is a shorthand for `typeof(null)`. Omitting the `Identifier` is allowed only when the `EnumMember` is the only one with its type and the declaration is not anonymous. Basically we have these categories of sum types: * Optionals: 1 member (usually unnamed) plus `null`. * Non-nulls: 1 member (usually unnamed) of reference type minus `null` (= plus `!null`). * Commutative: ≥ 2 members (usually unnamed) with different types. * Homogeneous: ≥ 2 named members with the same type. * Algebraic: All members named, potentially with repeated types, potentially self-referential. * Non-null commutative: ≥ 2 members (usually unnamed) with different types among which are reference types plus `!null` * Non-null algebraic: All members named, potentially with repeated types among which are reference types (potentially self-referential) plus `!null`. A member can be an untyped named constant. Those are equivalent to members of unique unit type. But if types are optional and identifiers are optional, how do we know which is which? We don’t. We need something to disambiguate. I went with `case` for something that’s maybe acceptable. Here, `case` can be read as a type: “Make a unique case type for this.” The `case` in `case x = init` is `Case_x` defined as `struct Case_x { typeof(init) value = init; }`. Because the type is a unit type, its value need not be stored in the instance. If all members are `case` members, the type is effectively a plain-old `enum`. The union (not the tag field) can be elided as the tag field suffices to retrieve the values. The only difference to regular `enum` I see is that the DIP proposes that for the tag field, the smallest available integral type be used, whereas `enum` by default has `int` values. I call the (usually unnamed) type-distinguished sum types commutative because there’s no reason whatsoever to treat `int + string` and `string + int` differently. There is *the* `int` member and *the* string member. It’s the same type spelled differently the same way ordering of type constructors or function attributes (on e.g. function pointer types) not only makes no difference, but creates the very same type. One does not simply reorder a struct, but one can reorder a union and commutative sum types are close enough. Algebraic sum types are too much struct-like to be reordered (naming matters, knowing the type to extract does not suffice). The member querying syntax is okay, I guess it can be improved. While `value.member?` reads best, it meaningfully conflicts with the trinary operator, so the second best would be `value.?member`. If a sum type is an optional type, i.e. it is `enum union X {null, T}` for some `T`, it’d be great to have some returns an optional string: `null` if there is no cat and the cat’s name if there is a cat. This conflicts with the trinary operator as well, but the `?` followed by module-scope `.` without a space should be virtually non-existent. For a nullable value `v`, `v ?? d` returns `v` if it’s not `null` (but typed as not null) and `d` (default, lazy evaluated) if it is. A `.` could implicitly be `.?` if the member after it is followed up by `??` or `?.`, i.e. `value.member?.toString()` is actually `value.?member?.toString()` and `yourpet.cat ?? mycat` is actually `yourpet.?cat ?? mycat` because you might not have a cat. Querying for a commutative sum type would usually be done via the type: The sum type converts implicitly to an optional of any of its constituent types: ```d // object.d enum union SumType(Ts...) { Ts } enum union NotNull(Ts...) if (/* any is reference type */) { !null, Ts } enum union Nullable(T) if (/* T is not reference type and has no opNull */) { null, T } // main.d StringOrInt = SumType!(string, int); StringOrInt stringOrInt; if (string? s = stringOrInt) { } else (int? i = stringOrInt) { } else assert(0); ``` Also, `stringOrInt is int` and `stringOrInt !is int` would work. For an associative array, `aa[key]` can return an optional value instead of throwing a `RangeError`. That would enable `aa[key] ?? d`; and `aa[key] ??= value` can set the value only if `key` is not present already. For a (nullable) pointer `ptr`, the dereference expression `*ptr` can be treated like an optional value if it is followed by `??` or `?.`, and ptr?.member I see one problem with `?.` vs. `.?`, but the rule is easy: The question mark is where the optional is. For `.?`, the left operand is a concrete value and the member on the right side might or might not be present. If optional sum types are a common thing, maybe we need `?.?`: `lhs?.?member` is: “Give me the member’s value if `lhs` isn’t `null` and `member` is there; otherwise give me `null`.” Sorry for the long post.
Nov 30 2022
next sibling parent zjh <fqbqrr 163.com> writes:
On Wednesday, 30 November 2022 at 14:20:31 UTC, Quirin Schroll 
wrote:
 Sorry for the long post.
You should post a separate `article` and give a `'link'` here `Long articles` all should be like this.
Nov 30 2022
prev sibling parent reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Wednesday, 30 November 2022 at 14:20:31 UTC, Quirin Schroll 
wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!
3. As for a keyword, you could circumvent the problem by re-using existing keywords. `enum union` would be a candidate.
+1 for not introducing new keywords for such a feature. The sum type (or tagged union) in the current DIP looks somewhat like an extended `union`, so either combination of keywords containing `union` will work just great IMO. If there is an urge to add new keywords, it could be like: ```d tagged union Option(T) { None, T val } ``` or maybe `union alias` or something like that. Another essential and already mentioned feature is extensibility: imagine we have a typed event bus of the following configuration: ```d alias Events = SumType!(Connected, Disconnected, TextMessage); ``` Now we want to extend it with new types of events without losing support in the parts of the code we don't control (e.g. library code, etc.). How would we do that? One thing would be to redefine the type by hand: it works great when the number of options is low, but what if there are tens or maybe hundreds of types? Right now it's only possible by using classes, or by introducing a struct with discriminator/tag and a union field manually checking (and casting) the types at runtime. --- Also, as the current proposal implements the algebraic data type as a C-style `union` rather than a type alias (or something like that), giving an explicit name to each tag in the union may be overwhelming if there are many types to be included.
Dec 05 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 5 December 2022 at 14:17:47 UTC, Jacob Shtokolov wrote:
 Another essential and already mentioned feature is 
 extensibility: imagine we have a typed event bus of the 
 following configuration:

 ```d
 alias Events = SumType!(Connected, Disconnected, TextMessage);
 ```

 Now we want to extend it with new types of events without 
 losing support in the parts of the code we don't control (e.g. 
 library code, etc.). How would we do that?

 [...]

 Right now it's only possible by using classes, or by 
 introducing a struct with discriminator/tag and a union field 
 manually checking (and casting) the types at runtime.
IMO if you want this kind of extensibility, a sum type is the wrong tool for the job. This is *exactly* the use case that classes are designed for: extending existing code to operate on new types, without modifying the code itself. Sum types, by contrast, excel when the set of types is fixed, but you would like to be able to define new operations on that set of types without changing existing code.
Dec 05 2022
parent reply Jacob Shtokolov <jacob.100205 gmail.com> writes:
On Monday, 5 December 2022 at 14:50:47 UTC, Paul Backus wrote:
 IMO if you want this kind of extensibility, a sum type is the 
 wrong tool for the job. This is *exactly* the use case that 
 classes are designed for: extending existing code to operate on 
 new types, without modifying the code itself.

 Sum types, by contrast, excel when the set of types is fixed, 
 but you would like to be able to define new operations on that 
 set of types without changing existing code.
That would be the case, but D has things like BetterC which doesn't support classes (okay, it supports C++ classes, but still). There are other cases where one might need to extend such sum types, say, you want to extend error types, etc.
Dec 05 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 5 December 2022 at 17:31:11 UTC, Jacob Shtokolov wrote:
 On Monday, 5 December 2022 at 14:50:47 UTC, Paul Backus wrote:
 IMO if you want this kind of extensibility, a sum type is the 
 wrong tool for the job. This is *exactly* the use case that 
 classes are designed for: extending existing code to operate 
 on new types, without modifying the code itself.

 Sum types, by contrast, excel when the set of types is fixed, 
 but you would like to be able to define new operations on that 
 set of types without changing existing code.
That would be the case, but D has things like BetterC which doesn't support classes (okay, it supports C++ classes, but still).
If the problem is that BetterC has poor support for classes, then the solution should be to improve BetterC's support for classes (e.g., by making D classes less dependent on `TypeInfo`).
 There are other cases where one might need to extend such sum 
 types, say, you want to extend error types, etc.
The Rust community has come up with a variety of techniques to handle cases like this using library code. I am confident that D, with its powerful metaprogramming and reflection, is capable of handling them just as well, if not better.
Dec 05 2022
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Monday, 5 December 2022 at 18:41:39 UTC, Paul Backus wrote:
 On Monday, 5 December 2022 at 17:31:11 UTC, Jacob Shtokolov 
 wrote:
 On Monday, 5 December 2022 at 14:50:47 UTC, Paul Backus wrote:
 IMO if you want this kind of extensibility, a sum type is the 
 wrong tool for the job. This is *exactly* the use case that 
 classes are designed for: extending existing code to operate 
 on new types, without modifying the code itself.

 Sum types, by contrast, excel when the set of types is fixed, 
 but you would like to be able to define new operations on 
 that set of types without changing existing code.
That would be the case, but D has things like BetterC which doesn't support classes (okay, it supports C++ classes, but still).
If the problem is that BetterC has poor support for classes, then the solution should be to improve BetterC's support for classes (e.g., by making D classes less dependent on `TypeInfo`).
 There are other cases where one might need to extend such sum 
 types, say, you want to extend error types, etc.
The Rust community has come up with a variety of techniques to handle cases like this using library code. I am confident that D, with its powerful metaprogramming and reflection, is capable of handling them just as well, if not better.
Zig and Go does it better, they don't need any "library" "reflection" "metaprogramming", i would never use a language that needs a library to work with errors, it's tasteless
Dec 06 2022
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 6 December 2022 at 14:44:03 UTC, ryuukk_ wrote:
 On Monday, 5 December 2022 at 18:41:39 UTC, Paul Backus wrote:
 On Monday, 5 December 2022 at 17:31:11 UTC, Jacob Shtokolov 
 wrote:
 There are other cases where one might need to extend such sum 
 types, say, you want to extend error types, etc.
The Rust community has come up with a variety of techniques to handle cases like this using library code. I am confident that D, with its powerful metaprogramming and reflection, is capable of handling them just as well, if not better.
Zig and Go does it better, they don't need any "library" "reflection" "metaprogramming", i would never use a language that needs a library to work with errors, it's tasteless
You don't *need* a library, but that doesn't mean it can't be useful to have one. For example: in D, you can write all of your `throw` and `catch` and `scope (exit)` statements by hand if you want, but many users prefer to use library functions like [`enforce`][1] and [`ifThrown`][2] to simplify their code. In fact, it's kind of funny that you bring up Go as an example here, because many Go programmers use libraries like [`errors`][3] and [`xerrors`][4] that build on top of the built-in error handling features to make them more convenient and ergonomic. Of course, you don't *need* them, just like you don't *need* `enforce` in D, but they are still nice to have. [1]: https://phobos.dpldocs.info/std.exception.enforce.1.html [2]: https://phobos.dpldocs.info/std.exception.ifThrown.1.html [3]: https://pkg.go.dev/errors [4]: https://pkg.go.dev/golang.org/x/xerrors
Dec 06 2022
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 6 December 2022 at 17:46:47 UTC, Paul Backus wrote:
 On Tuesday, 6 December 2022 at 14:44:03 UTC, ryuukk_ wrote:
 On Monday, 5 December 2022 at 18:41:39 UTC, Paul Backus wrote:
 On Monday, 5 December 2022 at 17:31:11 UTC, Jacob Shtokolov 
 wrote:
 There are other cases where one might need to extend such 
 sum types, say, you want to extend error types, etc.
The Rust community has come up with a variety of techniques to handle cases like this using library code. I am confident that D, with its powerful metaprogramming and reflection, is capable of handling them just as well, if not better.
Zig and Go does it better, they don't need any "library" "reflection" "metaprogramming", i would never use a language that needs a library to work with errors, it's tasteless
You don't *need* a library, but that doesn't mean it can't be useful to have one. For example: in D, you can write all of your `throw` and `catch` and `scope (exit)` statements by hand if you want, but many users prefer to use library functions like [`enforce`][1] and [`ifThrown`][2] to simplify their code. In fact, it's kind of funny that you bring up Go as an example here, because many Go programmers use libraries like [`errors`][3] and [`xerrors`][4] that build on top of the built-in error handling features to make them more convenient and ergonomic. Of course, you don't *need* them, just like you don't *need* `enforce` in D, but they are still nice to have. [1]: https://phobos.dpldocs.info/std.exception.enforce.1.html [2]: https://phobos.dpldocs.info/std.exception.ifThrown.1.html [3]: https://pkg.go.dev/errors [4]: https://pkg.go.dev/golang.org/x/xerrors
Oh ok "to enhance" them, the part i missed
Dec 06 2022
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 6 December 2022 at 14:44:03 UTC, ryuukk_ wrote:
 Zig and Go does it better, they don't need any "library" 
 "reflection" "metaprogramming", i would never use a language 
 that needs a library to work with errors, it's tasteless
Let's get one thing clear in this thread, if nothing else: the Go error handling model is not one that anyone should be copying. It's verbose, unsafe, dysfunctional, and nobody should be pointing to it as an example of good design.
Dec 06 2022
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 6 December 2022 at 18:07:21 UTC, Meta wrote:
 On Tuesday, 6 December 2022 at 14:44:03 UTC, ryuukk_ wrote:
 Zig and Go does it better, they don't need any "library" 
 "reflection" "metaprogramming", i would never use a language 
 that needs a library to work with errors, it's tasteless
Let's get one thing clear in this thread, if nothing else: the Go error handling model is not one that anyone should be copying. It's verbose, unsafe, dysfunctional, and nobody should be pointing to it as an example of good design.
I agree with you, i was referring to having error as built-in as opposed to having them as library, but i missed the "to enhance them" part
Dec 06 2022
prev sibling next sibling parent reply Guillaume Piolat <first.last spam.org> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Would prefer WASM and named arguments to sum types.
Nov 30 2022
parent reply Hipreme <msnmancini hotmail.com> writes:
On Wednesday, 30 November 2022 at 20:01:47 UTC, Guillaume Piolat 
wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Would prefer WASM and named arguments to sum types.
Would love to implement Web support for my engine too :D
Nov 30 2022
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Wed, Nov 30, 2022 at 08:32:26PM +0000, Hipreme via Digitalmars-d wrote:
 On Wednesday, 30 November 2022 at 20:01:47 UTC, Guillaume Piolat wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Would prefer WASM and named arguments to sum types.
Would love to implement Web support for my engine too :D
I haven't had time to look at the two existing wasm projects yet (one by Adam, and the other I forgot by who); have you tried either of them out yet? How complete/incomplete are they? How much can you do in them? How close/far are they to a comprehensive WASM support package for D? I think we should evaluate what we currently have, and lay out a rough plan for how to move forward. My ideal dream is a WASM support package (build system, helper scripts, HTML/JS generators, etc) that lets me take an existing D program, complete with GC and main(), and compile it into a web app (WASM modules + HTML + JS glue, the latter preferably completely auto-generated, that can be copied into my Apache web directory and it would Just Work(tm)), with minimal / no changes to the code itself. Fiddly details like passing strings to/from JS, interfacing with web APIs or WASI or whatever, should just be completely automated away by scripts or build system or whatever. Of course, I think we still have a way to go before we get to this point. But it would be good to evaluate just where exactly we are right now wrt this ideal dream, so that we can make meaningful steps forward. T -- Why is it that all of the instruments seeking intelligent life in the universe are pointed away from Earth? -- Michael Beibl
Nov 30 2022
next sibling parent Hipreme <msnmancini hotmail.com> writes:
On Wednesday, 30 November 2022 at 20:54:26 UTC, H. S. Teoh wrote:
 On Wed, Nov 30, 2022 at 08:32:26PM +0000, Hipreme via 
 Digitalmars-d wrote:
 On Wednesday, 30 November 2022 at 20:01:47 UTC, Guillaume 
 Piolat wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!
 
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Would prefer WASM and named arguments to sum types.
Would love to implement Web support for my engine too :D
I haven't had time to look at the two existing wasm projects yet (one by Adam, and the other I forgot by who); have you tried either of them out yet? How complete/incomplete are they? How much can you do in them? How close/far are they to a comprehensive WASM support package for D? I think we should evaluate what we currently have, and lay out a rough plan for how to move forward. My ideal dream is a WASM support package (build system, helper scripts, HTML/JS generators, etc) that lets me take an existing D program, complete with GC and main(), and compile it into a web app (WASM modules + HTML + JS glue, the latter preferably completely auto-generated, that can be copied into my Apache web directory and it would Just Work(tm)), with minimal / no changes to the code itself. Fiddly details like passing strings to/from JS, interfacing with web APIs or WASI or whatever, should just be completely automated away by scripts or build system or whatever. Of course, I think we still have a way to go before we get to this point. But it would be good to evaluate just where exactly we are right now wrt this ideal dream, so that we can make meaningful steps forward. T
Adam's WASM is a lot easier to a mere mortal to use, it is only a matter of including it to the project and build it as ldc wasm triple and you're done. I think the problem Adam's project has is of it being a custom druntime so you'll have a lot of pitfalls there and must be careful in which features you're using, but it is quite usable small projects IMO. The other is from Sebastian Koppe, which he attempts to port WASI as the libc (and thus, making the runtime just work). The problem is that you will need to actually build the druntime and wasi itself which I wasn't able to get it done for reasons like: WASI wasn't building in my PC so I wasn't able to try the runtime. From what I remember that Adam said, there was a problem on the garbage collection being executed while wasm was running its code. IIRC, he said something about deferring the GC, didn't quite get the details but he seemed really sure that would solve the problems.
Fiddly details like passing strings to/from JS interfacing with 
web APIs or WASI or whatever, should just be completely 
automated away by scripts or build system or whatever.
IIRC, Adam's project had an `eval!` which would be able to do that. But I guess having real compiler support and marking the function as `extern(System)` could do that. Executing JS defined code could even be: ` wasm("the.javascript.function")` or even `extern(WebAssembly, "the.javascript.function")` as it would use the same syntax from the C++ namespace
Dec 01 2022
prev sibling parent reply Guillaume Piolat <first.last spam.org> writes:
On Wednesday, 30 November 2022 at 20:54:26 UTC, H. S. Teoh wrote:
 I haven't had time to look at the two existing wasm projects 
 yet (one by Adam, and the other I forgot by who); have you 
 tried either of them out yet?
With the LDC wasm triple, Phobos doesn't build on Windows, I didn't go much further. Beyond that, any C function will also not work.
 I think we should evaluate what we currently have, and lay out 
 a rough plan for how to move forward.
It seems to me there is few people that know what we do have, or can even envision the plan.
Dec 01 2022
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 1 December 2022 at 11:51:23 UTC, Guillaume Piolat 
wrote:
 On Wednesday, 30 November 2022 at 20:54:26 UTC, H. S. Teoh 
 wrote:
 I haven't had time to look at the two existing wasm projects 
 yet (one by Adam, and the other I forgot by who); have you 
 tried either of them out yet?
With the LDC wasm triple, Phobos doesn't build on Windows, I didn't go much further. Beyond that, any C function will also not work.
 I think we should evaluate what we currently have, and lay out 
 a rough plan for how to move forward.
It seems to me there is few people that know what we do have, or can even envision the plan.
From a language PoV, WASM support is 100% complete From a runtime/std PoV it's not.. but if you are making a game it doesn't matter AT ALL! In fact, i am even working on a game that supports WASM target using WebGL: https://www.kdom.xyz/ (it is an online multiplayer game) I haven't updated the build on the website for a while since i'm focusing on the art/backend side of things, but i'll soon make a proper blog post about it once it's in a pleasant and playable state My only issue so far was when certain features didn't work, and the compiler wouldn't help me with error messages, for example: working with slices I had to copy bits of druntime's object.d, i wish i didn't have to do that But i learned to like it and now i have a custom object.d and i can use: `i32 u32 i64 u64`(etc) aliases globally! That's pretty cool Oh.. and allocating memory with WASM, this is why i wished there was a proper Allocator API available that is minimal (meaning it doesn't import anything from either druntime/phobos), so it's available right away to kickstart projects that targets esoteric platforms such as WASM So i made one! it's basically a port of Zig's one, i yoinked it since i didn't like the ergonomics of the language for their API is cool, so i went a head and ported it, i might release a dub package in the future We have to be careful with WASM, we should not offer a bloated solution Writing your javascript glue code is more than enough to access browser APIs, basic WASI support is all we really need if you want to make a game So a general purpose solution might not be the most useful one Look at Blazor from .net and how they struggle to produce small enough wasm files My wasm file is just 700kb (and it includes builtin textures/shaders), competitive advantage for D My glue code for WebSocket is just this: https://www.kdom.xyz/lib/net.js ```D // websocket alias ws_cb_t = extern(C) void delegate (int ws, int event, int data); int js_ws_create(const(char)[] url, void* bufferPtr, scope ws_cb_t cb); bool js_ws_send_data(int ws, const(void)* ptr, int len); bool js_ws_send_str(int ws, const(char)* ptr, int len); void js_ws_disconnect(int ws); extern(C) export void js_cb_ws(uint ctx, uint fun, int ws, int event, int data) { static struct Handler { union { extern(C) void delegate(int, int, int) handle; struct { void* contextPtr; void* funcPtr; } } } Handler c; c.contextPtr = cast(void*) ctx; c.funcPtr = cast(void*) fun; c.handle(ws, event, data); } ``` and i use it: ```D state.ws_id = js_ws_create(host.strip, cast(void*) state.ws_buffer.ptr, (ws, evt, data) { if (evt == 1) // onopen { state.net_info.state = ConnectState.CONNECTED; on_connected(); } else if (evt == 2) // data str { } else if (evt == 3) // data binary { decode_packet(state.ws_buffer[0 .. data]); } else if (evt == 4) // close { state.net_info.state = ConnectState.DISCONNECTED; state.ws_id = 0; on_disconnected(); } else if (evt == 5) // error { LERRO("ws: error {} {} {}", ws, evt, data); } else panic("shouldn't happen {} {} {}", ws, evt, data); }); ``` Full delegate support out of the box! I need to become more responsible and provide blog posts, resources and more examples on github to help people.. i got this one example but no documentation and is a bit too old: https://github.com/ryuukk/dasm Hopefully this game i am making will be a good showcase for D
Dec 01 2022
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 1 December 2022 at 12:48:04 UTC, ryuukk_ wrote:


Forgot to add, good ressources for WASM without emscripten, 
that's what i followed basically:

https://schellcode.github.io/webassembly-without-emscripten


Also an example of a language that follows this principle: Odin:

https://github.com/odin-lang/Odin/tree/master/vendor/wasm


This is imo the proper way to do it

And you can still use emscripten btw: 
https://theartofmachinery.com/2018/12/20/emscripten_d.html

So druntime/phobos might be working out of the box, but will 
produce rather large wasm files

I'll give that a try some day
Dec 01 2022
parent Hipreme <msnmancini hotmail.com> writes:
On Thursday, 1 December 2022 at 12:55:17 UTC, ryuukk_ wrote:
 On Thursday, 1 December 2022 at 12:48:04 UTC, ryuukk_ wrote:


 Forgot to add, good ressources for WASM without emscripten, 
 that's what i followed basically:

 https://schellcode.github.io/webassembly-without-emscripten


 Also an example of a language that follows this principle: Odin:

 https://github.com/odin-lang/Odin/tree/master/vendor/wasm


 This is imo the proper way to do it

 And you can still use emscripten btw: 
 https://theartofmachinery.com/2018/12/20/emscripten_d.html

 So druntime/phobos might be working out of the box, but will 
 produce rather large wasm files

 I'll give that a try some day
Actually it dont: `Instead of porting the D runtime, it uses a lightweight, runtimeless -betterC build.` Doing a simple game without GC is possible, but when you wish to use GC and standard library this is the problem. If one wishes to offer a not bloated solution, one can use betterC, having the option for me is the best thing to do. I have done my own standard library to use in D, which avoids many templates, many imports depending from the libc, still, it is quite far from having real support on WASM. Though I think the main problem it has is using associative arrays, I have my own implementation of ref counted associative arrays, but I'm not using. I should consider a bit about this.
Dec 01 2022
prev sibling next sibling parent RTM <riven baryonides.ru> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
What about changing the name? To avoid yet another inconsistency. Product type is “struct”, not “producttype”. variant, maybe?
Nov 30 2022
prev sibling next sibling parent reply cc <cc nevernet.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
```d if (?ret.Error) ``` I imagine some stubbed toes writing `if (ret.Error)` without the `?` for a while, and potential unnoticed bugs when it works by accident. I like the recentish trend of D compiler errors suggesting "Did you mean to use..." but would that be suitable for a runtime error message? [null-conditional operators](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditio al-operators--and-) someday? e.g. ```d class Foo {...} Foo a = new Foo; Foo b = null; auto res = a?.bar(); // Ok res = b?.bar(); // returns null (returntype.init for value types?) ```
Nov 30 2022
parent cc <cc nevernet.com> writes:
On Thursday, 1 December 2022 at 05:49:56 UTC, cc wrote:
 I imagine some stubbed toes writing `if (ret.Error)` without 
 the `?` for a while, and potential unnoticed bugs when it works 
 by accident.  I like the recentish trend of D compiler errors 
 suggesting "Did you mean to use..." but would that be suitable 
 for a runtime error message?
Nevermind, I see now it's just a straight up termination with no message. Ouch.
Nov 30 2022
prev sibling next sibling parent JN <666total wp.pl> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Looks nice. Don't like the question mark syntax though. It's going to be confusing especially when ternary operator comes into play and I'd constantly double-check myself whether ?x.allow is (?x).allow or ?(x.allow), even though the first one wouldn't make sense. Why not use "is" which is already a keyword? instead of: y = ?x.allow ? x.allow : Xyzzy.busy we'd have: y = x is allow ? x.allow : Xyzzy.busy which is more readable naturally and doesn't cause confusion when we ever add nullable types with ?. etc. Regarding the sumtype Option(T) = None | Some(T); syntax... I personally don't care much either way, but I feel the original syntax fits D better. Things like | for sumtypes or usingn -> for return type is something I associate with functional languages which feel more math-like in general, but in C-like languages I am more expecting of "traditional" syntax. But like I said, I don't really care either way, whichever is easier to parse and will confuse tools less.
Dec 02 2022
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics. I have come to believe over several years working in D, managing work in D at some scale (10s of devs, not 100s) & advocating for D that the foundations are shaky and crumbly and need work. While it is not possible to implement a struct as drop-in replacement for a built in slice, while template symbol emission bugs keep happening[1], while the built in AAs are not in great shape (and the signature of toHash is a PITA), while the compiler is a huge memory hog and compile times for templates get wild, while speculative compilation results somehow go wrong and leak in to the non-speculative parts of compilation (e.g. https://issues.dlang.org/show_bug.cgi?id=19545), while syntax changes happen without enough noise (so even basic tooling ends up breaking), while shared and property and synchronised classes and so on sit in poor and/or incomplete shape, while delegate contexts still have qualifier problems[2]… Why would the limited resources available be focused on a built-in sum type? D is an amazing language. “Another killer feature” isn’t the missing piece to it getting some real, persistent, large-scale traction. They key is the rock solid basics and the cleaning up of lingering nonsense. Then the features can come, if they need to, because everyone loves building features and the more the basics are sorted out the easier it will be to add more. *The really committed and talented people here need to choose quality and build it.* I think D has a culture of accepting all sorts of badness while promising greatness, which is self-limiting. I have poured a unbelievable number of hours in to D over the years, both commercially and open source, it has formed the backbone of my career, I want to see it succeed, but this *will not happen* if the basics are not dealt with. I’m happy talk more about this with any of the core developers, I may not write a lot more out here as I don’t want to keep ladling on negativity in public, but for a variety of reasons I think change is needed. [1] please no “where’s the bug report”, it’s whackamole. There needs to be a reckoning with the fundamental nature of the problem. Does *anyone* even understand the current design & implementation? [2] this is pretty much what I could think of off the top of my head. I’m sure there’s more!
Dec 03 2022
next sibling parent Bruce Carneal <bcarneal gmail.com> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics. I have come to believe over several years working in D, managing work in D at some scale (10s of devs, not 100s) & advocating for D that the foundations are shaky and crumbly and need work. ... *many* well reasoned points supporting the above ...
Well put.
 [1] please no “where’s the bug report”, it’s whackamole. There 
 needs to be a reckoning with the fundamental nature of the 
 problem. Does *anyone* even understand the current design & 
 implementation?
The benefits of an alternate front end development effort have never been clearer.
Dec 03 2022
prev sibling next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics. [...]
Yep. I 100% agree. The issue with D right now are not the bells and whistles. It's the bread and butter. As for the template emission, having been away from the issues and scene for a while made me realize the reason of the emission issues. It's that we cannot emit the same symbol twice. Ibuclaw if I am correct at least that would be solved by making template instances weak symbols no? If we did that we could remove some complexity from the emission.
Dec 03 2022
prev sibling next sibling parent bachmeier <no spam.net> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics.
I agree. The question is **who other than Walter is going to do this**. These are the things that, IMO, Walter should not even be part of.
Dec 03 2022
prev sibling next sibling parent CM <celestialmachinist proton.me> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics. [...]
SDC seems to be a pathway to a nicer frontend, at least. Would love to see its representation at the meetings.
Dec 03 2022
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
Its worth remembering that sum types and tuples are both foundational in 
type theory. Getting these into the language allow for the language to 
be a lot more foundationally stable in its design.

And thats coming from someone who has been screaming about DllImport 
being incomplete in dmd and running into issues about that almost daily 
(ok not daily because I have written off dmd until its fixed) and all 
the while working on a DIP for value type exceptions lol.
Dec 03 2022
next sibling parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Saturday, 3 December 2022 at 22:55:47 UTC, rikki cattermole 
wrote:
 Its worth remembering that sum types and tuples are both 
 foundational in type theory. Getting these into the language 
 allow for the language to be a lot more foundationally stable 
 in its design.
I agree with this 100%! Let's not get ourselves distracted, there are issues that needs to be resolved, sure, but we should not forget about the importance of having sumtype/tuple right
Dec 03 2022
prev sibling parent Kagamin <spam here.lot> writes:
On Saturday, 3 December 2022 at 22:55:47 UTC, rikki cattermole 
wrote:
 Its worth remembering that sum types and tuples are both 
 foundational in type theory. Getting these into the language 
 allow for the language to be a lot more foundationally stable 
 in its design.
Plenty of safe languages don't have sum types and sky doesn't fall.
Dec 05 2022
prev sibling next sibling parent reply zjh <fqbqrr 163.com> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics. I have come to believe over several years working in D, managing work in D at some scale (10s of devs, not 100s) & advocating for D that the foundations are shaky and crumbly and need work. While it is not possible to implement a struct as drop-in replacement for a built in slice, while template symbol emission bugs keep happening[1], while the built in AAs are not in great shape (and the signature of toHash is a PITA), while the compiler is a huge memory hog and compile times for templates get wild, while speculative compilation results somehow go wrong and leak in to the non-speculative parts of compilation (e.g. https://issues.dlang.org/show_bug.cgi?id=19545), while syntax changes happen without enough noise (so even basic tooling ends up breaking), while shared and property and synchronised classes and so on sit in poor and/or incomplete shape, while delegate contexts still have qualifier problems[2]… Why would the limited resources available be focused on a built-in sum type?
You should make a list sort by importance.
Dec 03 2022
parent zjh <fqbqrr 163.com> writes:
On Sunday, 4 December 2022 at 01:39:26 UTC, zjh wrote:
 You should make a list sort by importance.
`D` needs to explain the `d` core and internal mechanism in more detail! Then others will know how to modify it! And not the personal project of the `d` author. It is better to have a `text version` of the detail.
Dec 03 2022
prev sibling parent Kagamin <spam here.lot> writes:
On Saturday, 3 December 2022 at 16:17:19 UTC, John Colvin wrote:
 On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright 
 wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I don’t want to be discouraging of good work, but D is not lacking in great features, it is lacking in its implementation of the basics.
Rationale explains it as a safe union. Whether safe union is really needed is a different question, but at least it's consistent with the safety effort.
Dec 05 2022
prev sibling next sibling parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
Actually why not to introduce optional type first and then base sumtype implementation on optional saying that `sumtype_obj.value` yields to optional of type `typeof(sumtype_obj.value)?` Offtopic about strictly non-null pointers: what's about `int*! ptr` syntax? a pointer that can be null: `int*? ptr`. And the regular pointer (`int* ptr`) can be, for example, force-treated as `int*! ptr` or `int*? ptr` based on compiler option.
Dec 03 2022
prev sibling next sibling parent cc <cc nevernet.com> writes:
On Tuesday, 29 November 2022 at 06:26:20 UTC, Walter Bright wrote:
 Go ahead, Make My Day! Destroy!

 https://github.com/WalterBright/DIPs/blob/sumtypes/DIPs/1NNN-(wgb).md
I like the `a | b | c` syntax, because the simpler and cleaner it is to use a tool suitable for efficient expressive error checking, the more likely I'll be to *actually use it* to implement error checking. The seductive alternative in my experience will always be "Ehh, error checking? What a hassle. I'll wrap everything in try/catch tomorrow... maybe."
Dec 03 2022
prev sibling parent Kagamin <spam here.lot> writes:
Query expression probably overlaps with pattern matching. Maybe 
use keyword based syntax? `if(case(result.Error))...`
Dec 04 2022