DIP 1017--Add Bottom Type--Final Review

Johannes Loher johannes.loher at fg4f.de
Tue Jan 15 18:21:25 UTC 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]]).


More information about the Digitalmars-d mailing list