November 29, 2022
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.
November 29, 2022

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»?

November 29, 2022
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.
November 29, 2022
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
November 29, 2022
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
November 30, 2022
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
November 29, 2022
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).
November 29, 2022
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.
November 29, 2022
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]).
November 29, 2022
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)