[External] : Re: Changes to JEP 453

forax at univ-mlv.fr forax at univ-mlv.fr
Wed May 24 12:14:37 UTC 2023



----- Original Message -----
> From: "Ron Pressler" <ron.pressler at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "loom-dev" <loom-dev at openjdk.org>
> Sent: Wednesday, May 24, 2023 12:25:31 PM
> Subject: Re: [External] : Re: Changes to JEP 453

>> On 24 May 2023, at 10:31, forax at univ-mlv.fr wrote:
>> 
>> 
>> Here we are discussing about two things, the first one is to have a method
>> result() in Subtask, the second is exception transparency, i think even if you
>> no not want a method result() in Subtask (let see how people use the preview),
>> having exception transparency also simplify the code.
> 
> What you’re describing isn’t really exception transparency (to me, exception
> transparency means “I throw what this thing throws”, and so implies some kind
> of inference, which we could do in the result case but for the time being we
> want to encourage people to handle exceptions centrally). Even supposing we had
> a language feature that could allow specifying multiple exception types, e.g.
> `new StructuredTaskScope.ShutdownOnFailure<ExceptionA | ExceptionB>` you'd
> still have to declare all of the forks’ exception types in advance, because we
> fork different tasks in different calls to fork and we still couldn’t infer
> exception types to make them transparent.

???,
the idea is to declare explicitly the exception when creating the STS because Java is not able to do the union of several exceptions,
but the compiler has no problem to infer the type of the exception (which is a subclass of the one declared when creating the STS) when calling fork.

There is a slight hand trick to do which is to declare that the method of the functional interface has to propagate the inferred type *and* InterruptException, otherwise the compiler will always infer Exception (the common super type) if there is a blocking call inside the lambda.

  interface Invokable<T, E extends Exception> {
    T invoke() throws E, InterruptedException
  }

So yes, it should be nice to have the type system to be able to create the union of several exceptions but practically here, we do not need it.

> 
> As a result, the code is only simplified by the removal of `catch (Throwable e)
> { throw new RuntimeException(e); }`. It isn’t much of a simplification, and
> it’s also not what we want, because runtime exceptions will also be propagated
> without a good stack trace, and that’s probably what few want.

Nope, it also check that the exception are correctly handled at compile time and provide the right semantics so a user do not have to verify that the exception are correctly handled.
With your code below, if one Callable throw the wrong exception, the code compile, and will wrap the wrong exception inside a RuntimeException leading to the wrong error code to be propagated instead of saying to the user that the code is not valid.

> 
> Defining a method:
> 
>    static <T extends Exception> Function<Throwable, T> propagate(Class<T> ext) {
>        return e -> {
>            if (ext.isInstance(e)) return ext.cast(e);
>            if (e instanceof Error err) throw err;
>            else throw new RuntimeException(e);
>        };
>    }
> 
> And then using it like so,
> `scope.join().throwIfFailed(propagate(ApplicationException.class))`, will be
> just as simple of not more so, as we won’t have to specify an exception type
> argument if we don’t want to. If that’s useful (and I say “if", because you are
> giving up on a good stack trace), we could fold that functionality into another
> overload of throwIfFailed.


Handling developer mistakes at compile time, not at runtime is better IMO.
This is important in this particular case because exception paths in the code tend to be less tested.

> 
> — Ron

Rémi


More information about the loom-dev mailing list