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