January 16, 2019
There are at least five parts to this proposal:

a. Add a way to indicate that a function doesn't return.
b. Use a special return type to indicate that.
c. Make that type implicitly convert to absolutely every other type.
d. Make it a compile error to use that type in most ways.
e. Stop running catch/finally after assert errors, throwing exceptions,
etc. I believe this was poor phrasing instead of a real part of the
suggestion.

The proposal should be explicit about having these parts. I feel like the proposal came this far with as little objection as it did in part because it was not clearer.

The only points that are at all defended are (a) and (b), and the defense for (b) was sorely lacking.

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

If we add features merely because other languages do so, we are claiming that the designers of those languages are authorities to be blindly trusted. At that point, we can give up on the language and just use Rust or C++ or whatnot.

That said, it's worthwhile to examine how other languages implement similar features. We can see what works for them and what doesn't, and we can see how that might work for D.

The fact that the DIP does not have this analysis is surprising. Let me make up for this lack slightly.

Rust has a token called never, written !. It is not a type aside from syntactically. It has two functions: first, it indicates that the function doesn't return ("diverging", the documentation calls it). Second, it tells the compiler to infer the type from its later contextual usage. (Or at least, that's how it worked circa 2014. It might have some other mechanism for not showing type errors today.)

    #![feature(never_type)]
    fn temp() -> ! {
        panic!("I am panicking");
    }
    fn main() {
        let mut x : ! = temp();
        println!("x = {}", x);
        x = temp();
        println!("hello  world");
    }

Swift has a type called Never. It's used to indicate that a function doesn't return -- it panics or throws an exception. It doesn't have any implicit conversions. You can have variables of its type. You can pass it to functions. You can assign it to things. It doesn't support the ternary expression logic that this DIP proposes.

    enum HelloError : Error {
        case badName
    }
    func sayHello(name: String) throws -> Never {
        throw HelloError.badName
    }
    struct Foo {
        var never : Never
    }
    do {
        var n : Never
        n = try sayHello(name: "Todd")
        var foo : Foo = Foo(never: n)
    } catch {
    }

Kotlin has a type called Nothing. It doesn't do implicit conversions. It supports the ternary logic that this DIP proposes as a side effect (you can use throw and return in the same way). You can have variables of this type, pass it to functions, assign it to things, etc.

    fun foo(): Nothing {
        throw Exception("this doesn't work")
    }
    fun foo2(n: Nothing) {}
    fun bar() {
        try {
            var n: Nothing = foo()
            foo2(n)
            var n2 = if (true) { 12 } else { foo() }
        } catch (e: Exception) {}
    }

C++ uses an attribute and does nothing special with the function's return type.

Nim uses an attribute, likewise.

Go lets you add a documentation comment, but that's for humans, not the compiler. There may be internal, undocumented mechanism to tell the compiler that a particular function doesn't return, intended to be used only within the runtime; that's the sort of thing the Go team does.

I'd expect the proposal to also include the languages mentioned on the Wikipedia page for Bottom type that actually have a bottom type. (It has some egregious inaccuracies, such as claiming that Javascript has a bottom type named undefined.)

It seems like forbidding variables, function parameters, fields, etc of the no-return type would be unique for D. It's also mostly unique *within* D; only void works similarly. Had the author done this sort of review, it would have been an opportunity to notice that the proposal is unique in this way and reflect on the advantages and disadvantages.

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

This isn't true currently:

void main()
{
    scope (exit) writeln("scope exit");
    assert(0);
}

This prints "scope exit" as you'd expect. Destructors and catch/finally clauses are run too.

Is this intended as part of the proposal? If so, that would be a nontrivial change that would almost certainly be rejected. Or is it talking about code after the noreturn function? If so, that should be included in point 1, and it should be rephrased to clarify what "such branches" means.
January 16, 2019
On Tue, 15 Jan 2019 22:43:59 -0800, Walter Bright wrote:
> It reminds me of when we were coming up with a term for what became "immutable" types. People would say:
> 
> Q: what does 'readonly' actually mean?
> A: readonly means immutable
> Q: ok, I get it
> 
> Q: what does 'invariant' actually mean?
> A: invariant means immutable
> Q: ok, I get it
> 
> Finally we realized we were being clubbed with a Clue-By-4. Just call it "immutable" and voila, haven't had comprehensibility problems with it since.

In this case, people will ask: "what does bottom_t mean?"

Some people will first look at Wikipedia and see:

> In type theory, a theory within mathematical logic, the bottom type is the type that has no values. It is also called the zero or empty type, and is sometimes denoted with falsum (⊥).

That, like many Wikipedia articles, clarifies nothing. They might read on and see:

> In C, C++, Java, and Dart, the bottom type is void.

Which is wrong, and they'll wonder why the language has both bottom_t and void, and then they'll ask here.

And then we'll answer: it means this function doesn't return. Which suggests "NeverReturns" or some variation of that as the name of the type.
January 16, 2019
On Wednesday, 16 January 2019 at 06:43:59 UTC, Walter Bright wrote:
> On 1/15/2019 8:16 PM, aliak wrote:
>> +1 for not using "bottom". It's really confusing to anyone who is not a language theory expert.
>
> Fortunately, "bottom type" is very google-able, and this comes up:
>
> https://en.wikipedia.org/wiki/Bottom_type
>
> > "Never" makes a lot more sense.
>
> Inventing new jargon for established terms is worse. Established jargon gives the reader a certain level of confidence when established terms are used correctly in that the reader understands the text and that the writer understands the text.

How is it new jargon when other programming languages that are in far, far more widespread use than D are already using it? And why is it that none (i.e zero) of them went with bottom?

Do you honestly believe more readers will understand "returns bottom" over "returns never"? Honestly?

>
> The "Bottom" type appears in books on type theory, like "Types and Programming Languages" by Pierce. "Never" does not, leaving the reader wondering what relation it might have to a bottom type.

99.9% of programmers do not read those books, and bottom will.

s/bottom/never :p

>
> ---
>
> It reminds me of when we were coming up with a term for what became "immutable" types. People would say:
>
> Q: what does 'readonly' actually mean?
> A: readonly means immutable
> Q: ok, I get it
>
> Q: what does 'invariant' actually mean?
> A: invariant means immutable
> Q: ok, I get it
>
> Finally we realized we were being clubbed with a Clue-By-4. Just call it "immutable" and voila, haven't had comprehensibility problems with it since.

Immutable is obvious without googling. Is bottom?

Honestly, this sounds like one of those arguments of convenience -> "I like X so ill argue that it's the proper term" whereas if it were reversed, the argument would be "I like X because it makes more sense than the correct theoretical term that no one will understand" 🤷‍♂️
January 16, 2019
On Wed, 16 Jan 2019 04:24:34 +0000, aliak wrote:
> On Wednesday, 16 January 2019 at 01:14:58 UTC, Nicholas Wilson wrote:
>> Those can all be done by an attribute. So it comes down to a cost
>> benefit analysis: the benefits they provide are the same,
>> except the implementation of bottom is going to be much more costly.
> 
> Is it "much" more costly?
> 
> What about the benefit of not being able to write code like:
> 
> @noreturn int f() { ... } // wat?

That's a downside. Consider a binding processor for a scripting language. It accepts functions of the form:

    int fn(State* state);

where the return value indicates the number of return values pushed onto the stack. You want this processor to handle an `abort` function. You must declare it as:

    int abort(State* state);

You also want these functions to be able to call each other, and you want the `abort` function to have proper flow analysis when you do so. Therefore you must declare it as:

    noreturn abort(State* state);

This is obviously a problem. You either need two different functions with different names (and since both do the same thing, they'll be similar names, easy to confuse), or to forego the flow analysis benefits of noreturn.

It's a disincentive to use noreturn.

This might not be enough of a reason to use an attribute instead of a type, but it's *a* reason, and that kind of code has its place.
January 16, 2019
On Wednesday, 16 January 2019 at 07:04:37 UTC, Neia Neutuladh wrote:
> Kotlin has a type called Nothing. It doesn't do implicit conversions. It supports the ternary logic that this DIP proposes as a side effect (you can use throw and return in the same way). You can have variables of this type, pass it to functions, assign it to things, etc.
>
>     fun foo(): Nothing {
>         throw Exception("this doesn't work")
>     }
>     fun foo2(n: Nothing) {}
>     fun bar() {
>         try {
>             var n: Nothing = foo()
>             foo2(n)
>             var n2 = if (true) { 12 } else { foo() }
>         } catch (e: Exception) {}
>     }

This is not completely accurate. Nothing actually is the bottom type in Kotlin in the sense that it DOES implicitly convert to any type. E.g.

```
fun foo() : Nothing {
    throw Exception()
}

fun bar(s: String)
{

}

fun main(args: Array<String>) {
    bar(foo())
}
```

is a valid Kotlin program.

As you mention, you can however pass around variables of type `Nothing`. You can also somewhat do this with the type proposed by the DIP, but it is kind of limited: You can return expressions of type `Tbottom`, but you cannot declare a variable of that type or use it as parameter, similar to `void`. The reason for this is probably that in D every declared variable must by default be initiated to some value and this is not possible for void and `Tbottom`. However, I think this limitation is a bit arbitrary. We also have other types which do not have default initialization. Also this is valid D:

```
struct Tbottom {
    @disable this();

    this(const ref Tbottom t)
    {
    }

    @disable static Tbottom init() @property;
}

struct A {}

Tbottom fun()
{
    assert(0);
    return cast(Tbottom) A();
}

void main()
{
    Tbottom t = fun();
}
```

Granted, this still allows casting to `Tbottom` and this type is not implicitly convertible to every other type (these properties need to be baked into the language, I think) and you can still use the struct literal syntax to initialize variables of type `Tbottom`.

But I think this showcases that it is not necessary conceptually to disallow declaring variables of type Tbottom. They just need to be initialized to an expression of type `Tbottom`. They still can never have an actual value or be used because by definition, the expression they are initialized to never returns. The same is true for function parameters.

I actually also think it is correct to do the same for `void`, but `void` should even a single value and thus it should be default constructible and have an `init` property. As I mentioned earlier in this thread, this would make `void` a proper unit type, drawing a clear line between `Tbottom` and `void`. It is however very complicated to get this to work with C-interoperability.
January 16, 2019
On Tuesday, 15 January 2019 at 18:51:13 UTC, Johannes Loher wrote:
> [...]
18.
> A point was raised that Tbottom* == typeof(null) and Tbottom[] == typeof([]) would be preferable to both cases equating to Tbottom.

I totally agree with this criticism. What does `Tbottom*` mean conceptually? It is a type which can hold either an address where a `Tbottom` is located, or `null`. However, there cannot be any address where a `Tbottom` is located, because `Tbottom` does not have any values. This means that `Tbottom*` is a type which can hold exactly one value: `null`.

By the way, Kotlin does basically the same thing. Kotlin does not have pointers, but it has optional types and the type `Nothing?` is a type which can hold exactly one value: `null`. This also means that it is basically a unit type. While it is not actually Kotlin's `Unit` type, it could have been. Similarly, should we decide to go down that road and add proper top, bottom and unit types, we could define

```
alias Tbottom = typeof(assert(0));
alias void = Tbottom*;
```
January 16, 2019
On Wednesday, 16 January 2019 at 07:04:37 UTC, Neia Neutuladh wrote:
> There are at least five parts to this proposal:
>
> a. Add a way to indicate that a function doesn't return.
> b. Use a special return type to indicate that.
> c. Make that type implicitly convert to absolutely every other type.
> d. Make it a compile error to use that type in most ways.
> e. Stop running catch/finally after assert errors, throwing exceptions,
> etc. I believe this was poor phrasing instead of a real part of the
> suggestion.

Thanks for this analysis.
a. already exists as an attribute for LDC and GDC.
b. as you mentioned is not well argued
c, d & e follow from a bad argument

> The proposal should be explicit about having these parts. I feel like the proposal came this far with as little objection as it did in part because it was not clearer.
>
> The only points that are at all defended are (a) and (b), and the defense for (b) was sorely lacking.

On the contrary, there was lots of objection but no response. As to why it has managed to get this far through the DIP process is beyond me: none of the points in the draft review have been dealt with.

>> It's necessary to be competitive with other systems programming languages.
>
> If we add features merely because other languages do so, we are claiming that the designers of those languages are authorities to be blindly trusted. At that point, we can give up on the language and just use Rust or C++ or whatnot.

Indeed, I have a hard time imagining that point of rationale is serious on nature.

> That said, it's worthwhile to examine how other languages implement similar features. We can see what works for them and what doesn't, and we can see how that might work for D.
>
> The fact that the DIP does not have this analysis is surprising. Let me make up for this lack slightly.

Indeed, thanks for doing this.

January 16, 2019
On Wednesday, 16 January 2019 at 08:47:48 UTC, Johannes Loher wrote:
> On Tuesday, 15 January 2019 at 18:51:13 UTC, Johannes Loher wrote:
>> [...]
> 18.
>> A point was raised that Tbottom* == typeof(null) and Tbottom[] == typeof([]) would be preferable to both cases equating to Tbottom.
>
> What does `Tbottom*` mean conceptually? It is a type which can hold either an address where a `Tbottom` is located, or `null`. However, there cannot be any address where a `Tbottom` is located, because `Tbottom` does not have any values. This means that `Tbottom*` is a type which can hold exactly one value: `null`.

I think it was me who brought that pointer thing up in last review. Nicholas Wilson lambasted my idea, and rightfully, because I didn't come up with any practical use for null being defined like that. Well, now I have one: It could be used to finally let `null` to be assigned to `std.typecons.Nullable`.

> ```
> alias void = Tbottom*;
> ```

I believe you meant `enum null = typeof(Tbottom*).init;`


January 16, 2019
On Wednesday, 16 January 2019 at 09:19:13 UTC, Nicholas Wilson wrote:
> On the contrary, there was lots of objection but no response. As to why it has managed to get this far through the DIP process is beyond me: none of the points in the draft review have been dealt with.

I agree with this. It's okay to reject the proposed alternatives, but the DIP should, with some of the proposals at least, state why. Otherwise, the DIP should have no business to final review, if even to community review.
January 16, 2019
On Wednesday, 16 January 2019 at 06:43:59 UTC, Walter Bright wrote:
> On 1/15/2019 8:16 PM, aliak wrote:
>
> It reminds me of when we were coming up with a term for what became "immutable" types. People would say:
>
> Q: what does 'readonly' actually mean?
> A: readonly means immutable
> Q: ok, I get it
>
> Q: what does 'invariant' actually mean?
> A: invariant means immutable
> Q: ok, I get it
>
> Finally we realized we were being clubbed with a Clue-By-4. Just call it "immutable" and voila, haven't had comprehensibility problems with it since.

Yeah shame you skipped that discussion with enum and nothrow.