Union types and exception handling
Brian Goetz
brian.goetz at oracle.com
Thu May 14 13:00:30 UTC 2020
I assume you're trying to get to exception transparency for lambdas:
that's a worthy goal. We've been down this road before.
Union types are actually a pretty small part of the story here. One of
the roadblocks you're going to hit is that if you let someone say
"throws E", you're going to eventually want to let them say "catch E".
But E is erased, so what bytecode are you going to generate? How are
you going to tell an E from another exception? How do you even know
what order it will be allowed to put the catch blocks, since we
currently (for good reason) do not allow dominated catch blocks?
If you want to allow lambdas to throw checked exceptions, you also need
a story for keeping the exceptions from escaping the scope of compiler
analysis. The easy case is one like this:
interface ConsumerEx<T, E throws Exception> {
void accept(T arg) throws E;
}
<E extends Exception>
void forEach(ConsumerEx<T,E> c) throws E { ... }
Here, you assume that the implementation will just turn around and call
the lambda. But what if the implementation casts the lambda to raw, and
stashes it in a static, and calls it later (maybe from another thread)?
Now you've invented another "sneaky throw" vector.
It gets worse: think Streams. I might pass all sorts of throwy lambdas
to filter() and map(), but they don't get called until I call
`forEach()` or `collect()` (or worse, you call `iterator()` and then
call `Iterator::next`.) Capturing this soundly in the type system (an
effects system, actually) is no small bit of language engineering.
All of this is orthogonal to the union type / variadic generics part of
the problem.
On 5/14/2020 3:56 AM, Kłeczek, Michał wrote:
> Hello All,
>
> I am playing with javac lately looking for a way to implement some
> form of union types - along the lines of:
>
> interface Function<S, T, E extends Exception> {
> T apply(S arg) throws E;
> }
>
> interface Stream<T, E extends Exception> {
> // resulting stream is parametrized with a union type
> <S, MapperEx extends Exception, ResultEx extends E | MapperEx>
> Stream<S, ResultEx> map(Function<? super T, S, MapperEx> mapper);
> boolean isEmpty() throws E;
> }
>
> <S, T, E extends IOException | ClassNotFoundException> T
> foo(Function<S, T, E> f, S arg) throws ClassNotFoundException {
> try {
> return f.apply(arg);
> }
> catch (IOException e) {
> }
> }
>
> A very limited form of union types is already implemented for
> multi-catch clauses and I am wondering
> if:
> - it can be reused
> - anything more general has been discussed/prototyped - is anyone
> aware of such efforts?
>
> Thanks,
> Michal
>
More information about the amber-dev
mailing list