December 01, 2022
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.

December 02, 2022
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
}

December 02, 2022
On Friday, 2 December 2022 at 06:19:42 UTC, Max Samukha wrote:

> sumtype Error

*sumtype Result
December 02, 2022

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.

December 02, 2022

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

December 02, 2022

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

December 02, 2022
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.
December 02, 2022

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.

December 02, 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

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.
December 02, 2022

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.