Union types and exception handling
Kłeczek, Michał
michal at kleczek.org
Thu May 14 15:19:12 UTC 2020
Thanks for your response. Some thoughts inline.
On 14/05/2020 15:00:30, "Brian Goetz" <brian.goetz at oracle.com> wrote:
>I assume you're trying to get to exception transparency for lambdas:
>that's a worthy goal. We've been down this road before.
This is interesting - couldn't find any trace of discussions so I asked
here.
>
>
>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?
Actually I was more thinking about some "lightweight" (whatever that
means :) ) syntax extension to what we have today with generic
exceptions. For the reasons you state I don't think about generic catch.
>
>
>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.
Yes - and this is fine IMHO.
It is no different than what we have today.
>
>
>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.
We see more and more voices proposing getting rid of checked exceptions
entirely but I think something like:
Stream<T, E> {
<S, E1, E2 extends E | E1> Stream<S, E2> map(Function<T, S, E1>
mapper);
Iterator<T, E> iterator();
}
might be a pretty good middle ground.
<E extends IOException | ClassNotFoundException> Object readObject()
throws E;
would be erased to
Object readObject() throws Exception; //Exception is a lub of IOE and
CNFE
but would allow the compiler to perform some flow analysis similar to
what it does today with checked exceptions (isn't the exception list a
union type in disguise?)
If you have any links to past discussions - I am happy to read through
all of them.
Michal
>
>
>
>
>
>
>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