January 15, 2019
On Tuesday, 15 January 2019 at 12:20:20 UTC, Olivier FAURE wrote:

>
> I agree that this proposal should still be in the "Community Review stage". The DIP procedure document says:
>
>> At the end of the review period, the DIP Manager will work with the POC to ensure all feedback has been addressed, revise the DIP as necessary, and include a summary of the review round in the Reviews section of the DIP.
>
> While a "Reviews" section has been added, no other change has been added to the document. For instance, while the Reviews section notes that `Tbottom*` should logically be `typeof(null)`, the document body still lists `Tbottom*` == `Tbottom`.
>

"has been addressed" does not mean that the DIP will be revised to reflect feedback. What it means is I will ask the DIP author to respond to any feedback in the review thread and to make any changes the author feels should be made. It's ultimately up to the author whether or not to do either, but I won't move forward until I get a response to both requests. If the DIP was revised in response to any of the feedback, I will mention it in the summary.
January 15, 2019
On Tuesday, 15 January 2019 at 14:01:12 UTC, Mike Parker wrote:
> [snip]
>>
>
> "has been addressed" does not mean that the DIP will be revised to reflect feedback. What it means is I will ask the DIP author to respond to any feedback in the review thread and to make any changes the author feels should be made. It's ultimately up to the author whether or not to do either, but I won't move forward until I get a response to both requests. If the DIP was revised in response to any of the feedback, I will mention it in the summary.

There's a typo in the abstract that should get fixed at least...

"The primary purpose is to specify *vthe* return type of a function that does not return."
January 15, 2019
On Tuesday, 15 January 2019 at 08:59:07 UTC, Mike Parker wrote:
> DIP 1017, "Add Bottom Type", is now ready for Final Review. This is the last chance for community feedback before the DIP is handed off to Walter and Andrei for the Formal Assessment. Please read the procedures document for details on what is expected in this review stage:
>
> https://github.com/dlang/DIPs/blob/master/PROCEDURE.md#final-review
>
> The current revision of the DIP for this review is located here:
>
> https://github.com/dlang/DIPs/blob/4716033a8cbc2ee024bfad8942b2ff6b63521f63/DIPs/DIP1017.md
>
> In it you'll find a link to and summary of the previous review round. This round of review will continue until 11:59 pm ET on January 30 unless I call it off before then.
>
> Thanks in advance for your participation.


I'd like to point out that this proposal is introducing 2 new semantic features that are related but actually orthogonal.

1. Allow a functions to be marked as "no return"
2. Introduce a "bottom" type defined as typeof(assert(0)).  Any function that returns this type will be marked as "no return".

Other reviewers have asked for why using a "bottom" type is preferable to a function attribute or a special out contract. In regards to the first feature (allow functions to be marked as "no return") they are all "semantically equivalent".  They all provide a way for the control-flow analyzer to know when a function doesn't return and optimize the code surrounding it.

The second feature goes another step further by introducing the "bottom type" into the language.  This introduces new semantics that define how non-returning functions interact with the type system.  Consider the following:

int main(string[] args)
{
    return (args.length > 1) || assert(0);
}

This currently doesn't compile but will with the new "bottom" type.  Note that this example is another demonstration for why these 2 features are orthogonal.

So the question of whether we should use a type/function attribute/contract to mark functions as "no return" is a different question than whether we should be introducing a new bottom type.  The 2 are conflated because if we do introduce a new "bottom" type, then it means we will already have a way to mark a function as "no return".

I would first try to answer whether or not we want the "bottom" type.  If the answer is yes, then we now have a way to mark functions as "no return".  If the answer is no, then we can use a function attribute or special out contract.



January 15, 2019
On 15.01.19 14:31, Johan Engelen wrote:
> On Tuesday, 15 January 2019 at 11:18:20 UTC, Simen Kjærås wrote:
>> On Tuesday, 15 January 2019 at 10:59:40 UTC, Johan Engelen wrote:
>>> 4 - D already has a bottom type: `void`, why is a new type needed?
>>
>> Because `void` is not a bottom type - a bottom type has no values, while void has exactly one, and is thus a unit type. Proof: you can return from a void function (hence it has at least one value), and no information is transmitted through a void (hence it has at most one value).
> 
> Although I am not a mathematician, this argumentation feels broken. The argumentation of "a function returning bottom can never return" assumes mathematical functions (which must return a value), but in D a "function" is not necessarily a mathematical function. A function is a procedure,

But procedures are not outside the reach of mathematics. A procedure is a mathematical function that takes a state together with some arguments and gives you back a modified state. Its type is State×Args→State.
The syntactic sugar that imperative languages put on top of this does not make this any less true, it is just a distraction.

A procedure that returns a value gives you both the modified state and a value. Its type is State×Args→State×Ret. If Ret has no values then State×Ret has no values.

You have the following natural isomorphisms:

State×Unit ≈ State
and State×Bottom ≈ Bottom

So if your D function returns Unit, it is essentially a procedure, and if it has return type Bottom, it automatically does not return a state either. I.e. it just does not return.

The entire thing is completely uniform, there are no weird special cases.

> and can return no value, described by "void". For example, this is not allowed:
> ```
> void foo();
> void g() {
>    auto f = foo(); // not allowed, foo does not return any value
> }
> ```
> ...

But that's because the compiler explicitly checks for void. It's an unprincipled special case arising from the misunderstanding that void has no values.

It makes little sense to define a type "void" that has "no values" and then say "but as a special case, a function with return type void is a procedure instead", because you can just have a unit type. The whole "variables cannot be of type void"-nonsense is also nothing but annoying.

But of course you have a point. The only (and valid) reason to argue against the concept of a bottom type in D is that it tries to shoehorn a principled concept on top of the unprincipled and messy C legacy; as you said, you indeed lose compatibility, because C++ functions can both be annotated with [[noreturn]] and have a specified return type. Therefore, we might need both an attribute and a bottom type, such that the bottom return type automatically infers the attribute. You can also argue that putting only the attribute is "pragmatically" good enough, because it is needed anyway and "PL theory is useless" because you can just hack around non-uniformity with explicit special cases (like we have to do for void already).

> Interestingly, the proposal defines `a || b()` to be OK when `b()` returns Tbottom, but does not change that it is not OK when `b()` returns `void`...

I don't get why it would. What boolean value would you assign to a void b()?
January 15, 2019
This part is quite terse and I'm not sure what the implications are:

> A function that returns a Tbottom is covariant with a function that returns any type T if T is returned via the registers or if the function returning Tbottom is overriding a function returning T.

Would this work?

    class C
    {
      override Tbottom toString() { assert(false); }
    }

It's implied that that would work, but using more words and examples for clarity would be nice.

Would this print `int function()[immutable(char)[]]`, as it does today?

    import core.stdc.stdlib : exit;
    int doStuff();
    auto actions = ["quit": &exit, "stuff": &doStuff];
    writeln(typeof(actions.stringof));

It's not implied that that would work, and I strongly suspect it would not -- that it would find `&exit` and assume the delegates need to return Tbottom, then error because `doStuff` doesn't.
January 15, 2019
On Tue, 15 Jan 2019 10:59:40 +0000, Johan Engelen wrote:
> The proposal mentions the C std function `exit()`. With this proposal,
> we are still not able to directly call `exit()` and have the compiler
> understand that the function doesn't return. The declaration `extern(C)
> Tbottom exit();` won't mangle correctly, whereas `extern(C) void exit()
> @noreturn` would. In other words: interfacing with C needs to be
> described in the proposal.

C and C++ name mangling doesn't include return types for free functions, and that should be mentioned in the proposal. C++ does include return types for function pointers, though, so that's one area where this fails. The proposal should explicitly mention all this.

> In the discussion of the alternative `@noreturn`, it is claimed that "This has the awkward result of a function specifying it has a return type `T`, but never returns that type."  Such functions would simply return `void`, which I don't find awkward at all: `void` is a bottom type after all.

Or they will return a different type for structural reasons. For instance, I might use an external library that auto-exposes D functions to a scripting language but requires them to have a particular signature. Adding @noreturn wouldn't break that signature but would allow the compiler to do better flow analysis.

> Then it is mentioned that with `@noreturn` "Other potential uses of a bottom type will not be expressible", but there is no rationale or full description of those other uses. How is the 'bottomness' of `Tbottom` different from `void`?

void has one value you can only refer to implicitly and can't store in a variable. Tbottom has zero values, so it's even less usable than void.

> I would have imagined this proposal to be completely different: describe why having a new bottom type is useful, and then in a small extra paragraph mentioning that this new bottom type can also be used to describe nonreturning functions. A big addition like this needs a big justification with a solution to a major shortcoming. Not being able to specify a function never returning is just a very minor shortcoming.

Except GDC and LDC both let you do this already, so it's only a shortcoming for DMD.
January 15, 2019
On Tuesday, 15 January 2019 at 08:59:07 UTC, Mike Parker wrote:
> DIP 1017, "Add Bottom Type", is now ready for Final Review. This is the last chance for community feedback before the DIP is handed off to Walter and Andrei for the Formal Assessment. Please read the procedures document for details on what is expected in this review stage:
>
> https://github.com/dlang/DIPs/blob/master/PROCEDURE.md#final-review
>
> The current revision of the DIP for this review is located here:
>
> https://github.com/dlang/DIPs/blob/4716033a8cbc2ee024bfad8942b2ff6b63521f63/DIPs/DIP1017.md
>
> In it you'll find a link to and summary of the previous review round. This round of review will continue until 11:59 pm ET on January 30 unless I call it off before then.
>
> Thanks in advance for your participation.

Similar to other reviewers, I am very disappointed in the lack of adjustments that were made to the proposal after the previous reviews. It basically only says that there were concerns but they are invalid, without giving any reasons.

Unlike many other reviewers, I actually like the idea of having a bottom type very much (which might be related to me liking functional concepts in general very much). However, I still have severe concerns about the current proposal:

1.
> Introduce a bottom type which is a type that has no values.
>
> The primary purpose is to specify vthe return type of a function that does not return.

It is not clear at all how these two statements are related. It is absolutely possible to have a type with no values, which does not allow this, so it should be explicitly stated that the type being introduced will support this. Currently it sounds like it is a natural consequence, which it is not: `void` behaves like a bottom type in this regard—it has no value. I would prefer something like
"A bottom type—a type with no values—is introduced. Additionally, a function returning this type indicates that this particular function never returns."

Also I believe that the DIP should propose a name for the introduced bottom type. This would allow clearer formulations of the above statement:

"A bottom type—a type with no values— named `Tbottom` is introduced. Additionally, a function returning `Tbottom` indicates that this particular function never returns."

2.
> It's necessary to be competitive with other systems programming languages.

Why is this the case? In what way does not being able to specify that a function does not return prevent D from being competitive with other systems programming languages?

3.
> RAII cleanup, scope, finally, and catch clauses need not be generated for such branches.

Shouldn't this be better listed under "Smaller/faster code"? It is also "smaller code".

4.
> Properties of Tbottom:
>
> Tbottom.mangleof == "??";
> Tbottom.sizeof == 0;
> Tbottom.alignsize == 1;

Why are these values chosen? In particular, why `Tbottom.sizeof == 0;`, while `void.sizeof == 1;`? What is `alignsize` (I could not find that property on any other type)? What about other properties (`init`, `stringof`, ...)? In particular, it should be mentioned that `Tbottom.init` is an error (similar to `void.init`).

5.
> a || b
> a && b
>
> b may be of type Tbottom

I don't even understand what this means. Is it meant to express:
"In the expressions
```
a || b
a && b
```
`b` may be of type `TBottom`"
?

Why no mention of `a`? This makes it very confusing. From "A Tbottom is implicitly convertible to any other type. No other type is implicitly convertible to Tbottom." I am guessing, that `a` may also be of type `Tbottom`, but this is really confusing.

Also what is the type of the expressions?

6.
> a ? b : c
>
> If b and c are of type Tbottom, the result type is Tbottom.
> Else if b is Tbottom, then the result is typeof(c).
> Else if c is Tbottom, then the result is typeof(b).

Most points from 5. also apply here. At least the proposed types of the expression are defined. However, from a type theory point of view, they make sense only partially: If one of `b` and `c` is of type `Tbottom` and the other is not, the expression either returns `typeof(c)` or it never returns, i.e. it is of type `Tbottom`. So the correct type of the expression would be a sumtype of `Tbottom` and `typeof(c)`. D cannot express sumtypes at the moment, so the decision to simply use `typeof(c)` seems to be the pragmatic one. But the DIP should at least explain why this decision was made.

7.
> cast(T)expression
>
> is allowed if expression is of type Tbottom.

This should mention that the expression is allowed for any type `T`.

8.
> Any attempt to use the value of a Tbottom expression is an error.

What does "use the value of a `Tbottom` expression" mean precisely?

9.
> Conversion
>
> An expression of type Tbottom can be implicitly converted to any other type.

This is already stated above.

10.
> A function that returns a Tbottom is covariant with a function that returns any type T if T is returned via the registers or if the function returning Tbottom is overriding a function returning T.
>
> It is implementation defined if a type T is returned via the registers.

This sounds horrible to me. It basically means that you cannot expect a function that returns `Tbottom` to be covariant with any function that does not return `Tbottom` if you want to compiler agnostic, so why bother allowing it at all?

Also talking about registers at all in this DIP seems like a very bad smell to me. The concept of a bottom type is a high level concept and should not be concerned at all with the low level details of registers etc. (they are an implementation detail).

11.
> Alternative
>
> Use an attribute such as @noreturn. C++ uses this approach. This has the awkward result of a function specifying it has a return type T, but never returns that type. Other potential uses of a bottom type will not be expressible. An alias cannot be made from an attribute, and templates cannot accept attributes as arguments.
>
> The advantage to @noreturn is a function that does not return can be made covariant with a function that returns a value via a hidden pointer.

Some more explicit "Other potential uses" should be given. This is very vague. In particular, why would one want to have an alias for `Tbottom` if its only use is to indicate that a function never returns? What could be uses of `Tbottom` as template parameter? Also the potential uses should be mentioned in the rationale.

12.
> Currently, the return type of a function declared with an auto return type is inferred to be void when no value is returned. With this proposal, such functions may be inferred to return Tbottom.

Are there any implications to this? Are there any circumstances when the user might not want this? If not, it should be explicitly stated.

13.
> A common criticism that arose in the Draft Review and was repeated in this review was that the proposed feature would be better implemented as an attribute or pragma rather than as an aliased type.
>
> A suggestion was made that the proposal could be expanded to describe interactions with other language features and the implications thereof.
>
> Alternative names for the type alias were proposed, including: Bottom, bottom_t, never_t, never, nothing.
>
> A point was raised that Tbottom* == typeof(null) and Tbottom[] == typeof([]) would be preferable to both cases equating to Tbottom.
>
> The concern was raised that the feature would complicate the language implementation.

Aside from "A common criticism that arose in the Draft Review and was repeated in this review was that the proposed feature would be better implemented as an attribute or pragma rather than as an aliased type.", this DIP does not address any of these concerns.

About the naming issue: "`Tbottom`" is very inconsistent with the name of other "builtin" types. It is also very inconsistent with what other languages use. Here is a list of the names of the bottom types of some other languages which have bottom types similar to the proposed one:
- Scala: Nothing
- Kotlin: Nothing,
- Ceylon: Nothing,
- Flow: empty
- TypeScript: never
- Rust: !

I suggest that we use something that is consistent with other "builtin" types is not completely different from what other languages use.

14.
The DIP proposes to add a type which is located at the bottom of the type hierarchy, but there is no mention at all of the dual concept—a top type, i.e. a type which every type implicitly converts to. From a type theory point of view, this asymmetry seems really weird. I'd like the DIP to go into if this is being considered and if not, why. It should probably be done in a "Future work" section or something similar.

15.
If the proposed `Tbottom` type is introduced, it basically means we have 2 types that have no value: `Tbottom` and `void`. Until now, `void` was basically always used, when syntactically you need a type, but there is not actually a type. Unfortunately, the spec is very brief on `void`: "	no type". To cite Ali from his book:
"the keyword void represents having no type". `Tbottom` basically means the same, but it has additional semantic. I'd like the DIP to go into more detail where this leaves `void`.

16.
Maybe not really relevant for the Final Review, but I find the DIP to be very difficult to understand at several places. This not due to the fact that the topic is actually very complicated, but rather because of style of the used language. In particular, it seems that the DIP author avoided using complete sentences in a lot of places where using a well formulated sentence could have made the intended meaning very clear. The "Expressions" section is a particularly bad example (at least to me, it was only possible to understand it with a lot of guess work).



Outlook:

Suppose we add a bottom type to D. Type theory tells us that there cannot be two different bottom types. And this is true: `void` does not implicitly convert all other types, so `void` is not actually a bottom type. `void` could rather be considered to be a unit type, i.e. a type with exactly one value (which does not signal "nothing", but rather "no information"). Unfortunately it is not possible to access that value (i.e. you can't declare a variable of type void) in D at the moment.

In my opinion, if we decide to actually go the route of using types to represent information like "does not return", the proper way forward is to also properly support the related concepts, i.e. a top type (which is dual to a bottom type in regards to the type hierarchy) and a unit type (which is dual to a bottom type in a category theoretical sense: The unit type is the terminal object in the category of types and typed functions and the bottom type is the initial object in that category).

Properly defining `void` to be a unit type might lead to very difficult problems with C-interoperability because it would require us to actually give `void` a value and `void` has no value in C.

This might be an indicator that going that route is not the correct decision (as long as we care about C-interoperability, but I think that's a given). Consequently (applying the above paragraph) using the type system to signal things like "does not return" is NOT the correct way forward for D. This leaves us with the option of using an attribute like @noreturn, which is also very consistent with the C world: It is how this is implemented in C++ ([[noreturn]]).
January 15, 2019
On Tuesday, 15 January 2019 at 10:59:40 UTC, Johan Engelen wrote:
> On Tuesday, 15 January 2019 at 08:59:07 UTC, Mike Parker wrote:
>> DIP 1017, "Add Bottom Type", is now ready for Final Review.
>
> I know we are no longer supposed to discuss the proposal's merits, but...
>
> The proposal does not describe its merits. The only merit listed is being able to specify that a function does not return. The obvious choice is adding a function attribute for that, yet the proposal introduces a new magic type with a whole set of new problems of its own and only has a few lines of text on why an attribute would not cut it.

With all due respect to all involved in this conversation, this DIP should be almost entirely non-controversial, but it is not, because most people posting on this mailing list have a more practical programming background, rather than a theoretical one. When I say that, I am not excluding myself, so please don't interpret it as an insult.

Let's look at Rust: they have introduced a bottom type, denoted as !, and to my knowledge it was almost entirely without controversy, because that community has a much stronger theoretical focus than D. I know that D is not Rust, but in this instance I feel that we could learn a thing or two rather than grafting on some @noreturn hack (and it *is* a hack that just poorly emulates a bottom type).

The bottom type is well-understood and has already had all the kinks worked out by academia. It is more a less a free lunch, because somebody else has already done the heavy intellectual lifting for us.

> No rationale is given for:
> 1 - implicit conversion of Tbottom to other types
> 2 - being able to use a noreturn function in expressions `a || b`, `a && b`,  `a ? b : c`

There are good reasons for why _|_ behaves this way; in type theory, it is the subtype of all other types and thus can implicitly convert to them with no type soundness issues. However, I agree that we do not necessarily _need_ this functionality, thought it would be annoying to only implement a half-working bottom type. It would also reflect badly on D IMO.

> 3 - why a new _type_ is needed for describing a property of a function (that's what attributes are for)
> 4 - D already has a bottom type: `void`, why is a new type needed?

`void` is a unit type, and is not the same as a bottom type. It does not denote that a function does not return a value; it denotes that the function can only return exactly _one_ value. The fact that most C family languages do not implement unit types correctly is probably why this misconception comes about (I had the same misconception for a long time).

> "A function that returns a Tbottom is covariant with a function
<snip, no comment here>

> The proposal mentions the C std function `exit()`. With this
<snip, already answered in this thread>

> In the discussion of the alternative `@noreturn`, it is claimed that "This has the awkward result of a function specifying it has a return type `T`, but never returns that type."  Such functions would simply return `void`, which I don't find awkward at all: `void` is a bottom type after all.

This is incorrect, as has been mentioned elsewhere in this thread. It's the difference between 0 and 1; the bottom type has 0 values, while unit types have exactly 1 value.

> Then it is mentioned that with `@noreturn` "Other potential uses of a bottom type will not be expressible", but there is no rationale or full description of those other uses. How is the 'bottomness' of `Tbottom` different from `void`?

As stated above, Tbottom is the subtype of all types, while void is not. It's like asking how the number 0 is different from the number 1.

> I would have imagined this proposal to be completely different: describe why having a new bottom type is useful, and then in a small extra paragraph mentioning that this new bottom type can also be used to describe nonreturning functions.

To me, that doesn't make any sense, as the bottom type is THE canonical way to express that a function will not return. It is a type with 0 values, thus, intuitively, there is no possible way a function with a return type of Tbottom could ever possibly return. Thus, it must throw an exception, loop forever, or abort the program.

> A big addition like this needs a big justification with a solution to a major shortcoming. Not being able to specify a function never returning is just a very minor shortcoming.

I recommend reading up on what the bottom type is, and why it is useful in the context of a language's type system. Not only does it enable certain optimizations, it also has many other useful properties that we can take advantage of.

Stack Overflow can explain it better than me:

https://softwareengineering.stackexchange.com/questions/277197/is-there-a-reason-to-have-a-bottom-type-in-a-programming-language
January 15, 2019
On Tuesday, 15 January 2019 at 16:01:13 UTC, Jonathan Marler wrote:
> On Tuesday, 15 January 2019 at 08:59:07 UTC, Mike Parker wrote:
> I would first try to answer whether or not we want the "bottom" type.  If the answer is yes, then we now have a way to mark functions as "no return".  If the answer is no, then we can use a function attribute or special out contract.

+1
January 15, 2019
On Tue, 15 Jan 2019 18:21:25 +0000, Johannes Loher wrote:
>> Any attempt to use the value of a Tbottom expression is an error.
> 
> What does "use the value of a `Tbottom` expression" mean precisely?

assert(0)++, for instance, is not allowed.

It might not be allowed to write:

    void doNothing(T)(T value) {}
    doNothing(assert(0));

Depends on whether passing something to a function is considered "using" its value.

Combined with convertibility-style template constraints, this is going to explode in your face:

    void increment(T : long)(ref T value)
    {
        value++;
    }
    increment(assert(0));

While this example is contrived, it's inevitable that some people will run into it on occasion in more complex code, resulting in a lot of work to make every template bottom-safe.

> 14.
> The DIP proposes to add a type which is located at the bottom of the
> type hierarchy, but there is no mention at all of the dual concept—a top
> type, i.e. a type which every type implicitly converts to. From a type
> theory point of view, this asymmetry seems really weird. I'd like the
> DIP to go into if this is being considered and if not, why. It should
> probably be done in a "Future work" section or something similar.

As a library type, Variant, but that's not exactly within the type system.

> 16.
> Maybe not really relevant for the Final Review, but I find the DIP to be
> very difficult to understand at several places. This not due to the fact
> that the topic is actually very complicated,
> but rather because of style of the used language. In particular,
> it seems that the DIP author avoided using complete sentences in a lot
> of places where using a well formulated sentence could have made the
> intended meaning very clear. The "Expressions" section is a particularly
> bad example (at least to me, it was only possible to understand it with
> a lot of guess work).

Agreed. DIPs should use simple language and aim to use examples whenever possible. Brevity and sounding technical should be explicit non-goals.