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

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.

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

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

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

November 29, 2022

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

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

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?

sumtype EE
{
    T1; // Does T1 exist?
    struct T2 { string text; }
    struct T2 { int val; }
}

??