July 11, 2017
On 7/11/17 4:29 AM, Timon Gehr wrote:
> On 10.07.2017 18:23, Meta wrote:
>> ...
>> problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.
> 
> Bottom is just a regular type.

Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei
July 12, 2017
On 12.07.2017 03:50, Andrei Alexandrescu wrote:
> On 7/11/17 4:29 AM, Timon Gehr wrote:
>> On 10.07.2017 18:23, Meta wrote:
>>> ...
>>> problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.
>>
>> Bottom is just a regular type.
> 
> Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei

Every type is peculiar. That's essentially the point of having types.
There is not really a reason to invent a peculiarity ordering and then add additional special casing for types deemed more peculiar. (I.e., creating different types of types based on an informal judgment of peculiarity.)

In D, subtyping is messy anyway, as you cannot have a subtyping relationship between values with different memory layout. Hence in D, Bottom would not actually be a subtype of all other types.

In a very expressive type system, there are many more, functionally different types that are uninhabited, not just Bottom.
July 12, 2017
On 11.07.2017 19:27, Meta wrote:
> On Tuesday, 11 July 2017 at 08:29:12 UTC, Timon Gehr wrote:
>> On 10.07.2017 18:23, Meta wrote:
>>> ...
>>> problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.
>>
>> Bottom is just a regular type.
> 
> It's a regular type with unusual behaviour due to it being uninhabited.

All types are unusual. That does not mean they are not all types.
July 12, 2017
On 09.07.2017 23:45, Meta wrote:
> ...
> Another case that we should probably just statically disallow:
> ... > This obviously doesn't make any sense anyway
> ... > I don't see a reason for us to ever need to do that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
July 12, 2017
On Wednesday, 12 July 2017 at 09:32:32 UTC, Timon Gehr wrote:
> On 09.07.2017 23:45, Meta wrote:
>> ...
>> Another case that we should probably just statically disallow:
>> ... > This obviously doesn't make any sense anyway
>> ... > I don't see a reason for us to ever need to do that
> Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.

D is not ML or Haskell or Idris. Rust has trod this ground before us and they saw it prudent to not make ! a first-class type. I think this is both a concession to usability and an acceptable compromise for a systems programming language.
July 12, 2017
On 07/12/2017 05:32 AM, Timon Gehr wrote:
> On 09.07.2017 23:45, Meta wrote:
>> ...
>> Another case that we should probably just statically disallow:
>> ... > This obviously doesn't make any sense anyway
>> ... > I don't see a reason for us to ever need to do that
> Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.

Timon, I think you're very well positioned to author a DIP on this. Getting through acceptance on your static foreach DIP seems to not require a lot of extra work.

> Every type is peculiar. That's essentially the point of having
> types. There is not really a reason to invent a peculiarity ordering
> and then add additional special casing for types deemed more
> peculiar. (I.e., creating different types of types based on an
> informal judgment of peculiarity.)
I seem to recall Haskell calls those "kinds".

> In D, subtyping is messy anyway, as you cannot have a subtyping
> relationship between values with different memory layout. Hence in D,
> Bottom would not actually be a subtype of all other types.

It's a point, and it would make the implementation easier, but it would be a departure from theory. Also it makes user code a tad more awkward. Consider:

typeof(assert(0)) abort(const(char)[] message);
...
int fun()
{
   int x;
   ...
   return x != 0 ? 1024 / x : abort("Error: calculation went awry.");
}

I guess such expressions can be rewritten into separate statements:

if (x != 0) return 1024 / x;
abort("Error: calculation went awry.");

and then the compiler figures there's no need for a return following the call to abort.

Perhaps a solid plan is to start with a DIP that does not introduce conversion and then experiment with the result for a while. What do you think?


Andrei
July 13, 2017
On Wednesday, 12 July 2017 at 14:23:15 UTC, Andrei Alexandrescu wrote:
> On 07/12/2017 05:32 AM, Timon Gehr wrote:
>> On 09.07.2017 23:45, Meta wrote:
>>> ...
>>> Another case that we should probably just statically disallow:
>>> ... > This obviously doesn't make any sense anyway
>>> ... > I don't see a reason for us to ever need to do that
>> Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
>
> Timon, I think you're very well positioned to author a DIP on this. Getting through acceptance on your static foreach DIP seems to not require a lot of extra work.
> ...

I might do that, however there are a couple of open questions (see below).

>> Every type is peculiar. That's essentially the point of having
>> types. There is not really a reason to invent a peculiarity ordering
>> and then add additional special casing for types deemed more
>> peculiar. (I.e., creating different types of types based on an
>> informal judgment of peculiarity.)
> I seem to recall Haskell calls those "kinds".
> ...

Indeed, but the separation of types and kinds has no point.
See: https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell

It's perfectly fine to have a type of types which is its own type, especially if you allow non-termination.

>> In D, subtyping is messy anyway, as you cannot have a subtyping
>> relationship between values with different memory layout. Hence in D,
>> Bottom would not actually be a subtype of all other types.
>
> It's a point, and it would make the implementation easier, but it would be a departure from theory. Also it makes user code a tad more awkward.

I'm saying the D notion of subtyping is a bit messy because memory layout matters and subtyping and implicit conversion are not the same thing. Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype.

(Also, a language does not have to support subtyping to have an empty type.)

typeof(assert(0))* and typeof(assert(0))[] will hence be subtypes of all other pointer and array types respectively.

An issue is that we already have typeof(null). typeof(null) and typeof(assert(0))* are two ways to specify almost the same thing. One question is whether typeof(assert(0))* and typeof(null) should be the same, or if the former should not implicitly convert to class references.
I have also argued in the past that there should be a separate typeof([]). This role would now be filled by typeof(assert(0))[]. However, changing the type of '[]' may break code.

> Consider:
>
> typeof(assert(0)) abort(const(char)[] message);
> ...
> int fun()
> {
>    int x;
>    ...
>    return x != 0 ? 1024 / x : abort("Error: calculation went awry.");
> }
>
> I guess such expressions can be rewritten into separate statements:
>
> if (x != 0) return 1024 / x;
> abort("Error: calculation went awry.");
>
> and then the compiler figures there's no need for a return following the call to abort.
> ...

This code compiles and runs:

int x;
...
return x != 0 ? 1024 : (delegate int(){ assert(0,"Error: calculation went awry."); })();

> Perhaps a solid plan is to start with a DIP that does not introduce conversion and then experiment with the result for a while. What do you think?
> ...

I think the DIP should introduce conversion from the start, as conversion is easy to support. It is simple to support in the front end and the code gen for it is literally to emit nothing.


July 13, 2017
On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:
> Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype.
> ...

(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
July 13, 2017
On Saturday, 8 July 2017 at 20:27:11 UTC, H. S. Teoh wrote:
> Also, a @noreturn attribute would allow overriding a non-void class method with a @noreturn one (e.g. the derived class is a sentinel object that forces an exception / termination upon calling that method), whereas you can't do that with a None return type because the signature would not match the base class's.
>
>
> T

The reverse is also true, and more disastrous!
July 13, 2017
On 7/13/17 2:37 PM, Timon Gehr wrote:
> On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:
>> Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype.
>> ...
> 
> (Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)

I wonder if sizeof could be made size_t.max. -- Andrei