Money for Nothing, ...

Robbe Pincket robbepincket at live.be
Wed Jun 14 19:10:03 UTC 2023


David Alayachew
Wed Jun 14 13:00:06 UTC 2023

> Hello Rémi,
>
> To start off, Nothing is only going to take you a short distance. Nothing works decently enough for the type T. But for the Exception, Nothing will not work as a type parameter because the type parameter it is supposed to be matching is E, and E extends Exception. Nothing does not extend Exception (presumably), and even if it did, this would be a constantly growing problem that we do not want to put into the language. What if you need R to extend List\<String>, for example?


This is a flawed understanding of the question that was asked. A 'Bottom type' ('Nothing' here) is a type that extends every other type and is not instantiatable.

Brian Goetz
Wed Jun 14 17:50:53 UTC 2023

> [...]
> What you are probably reacting to here is "but why does Fail have to say T, it doesn't use it."  And the answer is: "get over that, and then you're done."
>
> You are trying to invent a new generics feature to avoid putting a `T` you don't use in your Error declaration.  But that T (and maybe E) are needed to unify the two under Result<T,E> -- just like in the second Haskell example above.

A valid reason in my opinion to want a `nothing` type is that it would solve the issue of unchecked casts your solution will introduce.

```
static Result<String> getContentOfFile(Path path) {
    // simple mock implementation
    if (!Files.exist(path)) {
        return new Fail<>(IOException("..."));
    } else {
        return new Succ<>("Data");
    }
}

static Result<List<String>> getLinesOfFile1(Path path) {
    return switch(getContentOfFile(path)) {
        case Succ<String>(var contents) -> new Succ<>(contents.lines().toList());
        case Fail<String> error -> (Result<List<String>>) error; // requires unchecked cast
    }
}

static Result<List<String>> getLinesOfFile2(Path path) {
    return switch(getContentOfFile(path)) {
        case Succ<String>(var contents) -> new Succ<>(contents.lines().toList());
        case Fail<String>(var error) -> new Fail<>(error); // requires redundant allocation
    }
}
```

One maybe slightly better solution is to introduce an `into` method on `Fail` to hide the cast

```
record Fail<T>(Throwable t) extends Result<T> {
    public <R> Fail<R> into() {
        return (Fail<R>)this;
    }
}


static Result<List<String>> getLinesOfFile3(Path path) {
    return switch(getContentOfFile(path)) {
        case Succ<String>(var contents) -> new Succ<>(contents.lines().toList());
        case Fail<String> error -> error.into();
    }
```

In the end, once we get value types, `getLinesOfFile2` wont really be an issue, (maybe someone even makes an extra optimization for sealed types where all subclasses are value types). Additionally, like Dan Smith said, a bottom type would only be useful here if have declaration-site variance, which we don't have (*yet*).

Kind regards
Robbe Pincket

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20230614/057c6d14/attachment.htm>


More information about the amber-spec-observers mailing list