Language feature to improve checked exceptions

Brian Goetz brian.goetz at oracle.com
Tue Mar 7 17:58:32 UTC 2023


Thanks for the well-reasoned thoughts.

Indeed, the monadic approach is more constraining and explicit (and has 
the accidental complexity of nominality, in that my Either won't 
interoperate with yours.)  Where I think we'd find differences of 
opinion is whether those constraints are a bug or a feature.  I agree 
that improving the ability to abstract over exception types is the more 
Java-like solution.

In reality, though, people will want both.  A good analogy is Streams.  
For the kinds of computations streams is good for -- and that's a lot -- 
people love it, it is clear, concise, reliable, and performant.  But it 
would be silly to say "let's get rid of all the control flow constructs 
in Java, and just do everything with streams" -- streams just isn't up 
to that task, and that's OK.  It's a flexible tool, and its good for 
what it is good for.

Unsurprisingly, Either is good in the same ways -- for some kinds of 
constrained composition, you can just >>= your way along and express 
things clearly and safely.  But just as we don't want to do all our 
computation with streams, we won't want to do all our computation this 
way either, and will want to punch out to a more imperative mechanism.

Finally, I'll point out that Streams, because of its inherent 
constraints, would work fine in either world, so we can't extrapolate 
very much from how it would work there.

Cheers,
-Brian

On 3/7/2023 11:05 AM, Holo The Sage Wolf wrote:
> I do want to write for the record my reasons to prefer the Effect 
> system approach in the hope to convince you and others about my view, 
> but I do acknowledge that this discussion is still far, and the 
> relevance of my words now may only appear when the work on patterns 
> and string templates will finalize, so unless there is a good reason I 
> will not continue the discussion further after this message.
>
> Effect System enables a code style that is easier to follow, it hides 
> the "unimportant" parts (unimportant to the logic) somewhere in the 
> background and allows you to write as if there are no effects to your 
> code (I am aware of the irony here). Of course it does in the end 
> force you to handle the effects, but you can handle those effects 
> orthogonally to your actual logic. Unlike the Monadic system which 
> requires you to work in "the monad world" as described a lot by 
> Haskell developers, you need to carry your Either wraps everywhere 
> till you handle them.
>
> In addition, I believe that there is another argument for the Effect 
> system specifically for Java: there is a difference between a language 
> feature and a datatype, unlike some other languages Java is built 
> heavily over 3rd party libraries, if we look at the big libraries out 
> there we would find that a lot of them are using custom Either (and 
> friends) classes, this cause that composing those libraries is not as 
> simple as we would want it to be, by making a language feature that 
> handle those cases /well/, there will be a much larger intersection 
> between the APIs (the "/well"/  in the previous sentence is important, 
> I want to believe that it is possible to do that in Java)
>
> Finally I want to link 2 more projects.
>
> Apart from Koka there is also [Effekt](https://effekt-lang.org/ 
> <https://urldefense.com/v3/__https://effekt-lang.org/__;!!ACWV5N9M2RV99hQ!Mfpw93goar8q9RTDRHUzTwShJx-j9c1yG0yNoeKm5PAcf_eUvgN0JS57Ou_dgr7E0c86MMVKHwb_tRs_3w$>), 
> I like the way Koka handles things better, but Effekt is another 
> language that has Effects as its core mechanic.
>
> Apart from Effect systems, there is also the idea of Coeffect systems, 
> for a similar reasons as above, I really like a Coeffect type system 
> and want to link to [my coeffect system in 
> Java](https://github.com/Holo314/Coeffect 
> <https://urldefense.com/v3/__https://github.com/Holo314/Coeffect__;!!ACWV5N9M2RV99hQ!Mfpw93goar8q9RTDRHUzTwShJx-j9c1yG0yNoeKm5PAcf_eUvgN0JS57Ou_dgr7E0c86MMVKHwZ7yoKdEw$>) 
> (I didn't have much time to work on it lately, but currently I am 
> fighting Java's compiler plugins API to make it a stand alone plugin, 
> as well as in the process of implementing custom type system in the 
> annotation to enable stuff similar to what I wrote in my previous mail).
>
> Again, I would love to discuss further when it is more relevant.
>
> On Tue, Mar 7, 2023 at 4:56 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>
>     Thanks for the link to Koka.
>
>     Indeed, I spent many hours prototyping something exactly like this
>     for Streams back in the Java 8 days, as an extension to one of the
>     "exception transparency" proposals that came up at the time.  As
>     you point out, in libraries like streams, a throwing lambda passed
>     as a parameter "pollutes" the type of the returned stream, since
>     the lambda is incorporated into the stream and may be invoked by a
>     later operation, and therefore might be thrown from that later
>     operation.
>
>     This basically asks the generic type system to have type variables
>     to track effects as well as types (see, e.g., "Type and Effect
>     Systems", Nielsen and Nielsen.)
>
>     As your example shows, it is possible to do this with (a) some
>     form of variadic generics and (b) some algebraic operations on
>     variadic parameters (e.g., concatenating, unioning,
>     differencing).  It also means that generic classes like Stream
>     have to carry around additional type variables describing their
>     exceptions (effects), and those tvars are "different" from
>     ordinary tvars.
>
>     Our conclusion at the time we did this experiment is that, while
>     the approach is viable, annotation burden on library authors is
>     high, and this burden flows through to clients as well.  (Even if
>     we allow use sites to elide the exception information, so they can
>     say Stream<String> rather than Stream<String, ExceptionGoop>, it
>     still flows into the docs and error messages.)  It also makes
>     generics "even more complex" (and people still complain generics
>     are too complex.)
>
>     So the outcome of this experiment was "yes, it can be made to
>     work, but no, we don't think putting into Java at this time is
>     likely to be seen entirely positively."
>
>>     While I do believe that the actual solution will require much
>>     more thought than this (e.g. deconstruction of generic sums to be
>>     able to handle specific exceptions and remove those exceptions
>>     from the final union type of exceptions), this is a showcase that
>>     a nice and composable checked exceptions works even without
>>     Monadic types.
>>
>
>     Indeed. The need for asymmetric difference as one of the algebraic
>     operations on effect variables is one of those surprises that
>     bites you the first time you encounter it.
>
>>     I find this direction much nicer than using Either everywhere.
>>
>
>     I am not sure whether I do or not, which is one reason we've not
>     done either yet...
>
>
>
>     On 3/7/2023 8:24 AM, Holo The Sage Wolf wrote:
>>
>>     This is a continuation of this thread
>>     <https://mail.openjdk.org/pipermail/amber-dev/2023-March/007849.html>,
>>     unfortunately I only joined the mailing list, so I can’t reply to
>>     it so I apologize for opening a new thread.
>>
>>     I want to point to Koka
>>     <https://urldefense.com/v3/__https://koka-lang.github.io/koka/doc/book.html*why-effects__;Iw!!ACWV5N9M2RV99hQ!Mfpw93goar8q9RTDRHUzTwShJx-j9c1yG0yNoeKm5PAcf_eUvgN0JS57Ou_dgr7E0c86MMVKHwaBnm23ow$>,
>>     Koka is a language built upon the concept of Effect, a
>>     generalization of how Java treats checked exceptions (and
>>     similarly to what Brian Goetz said about how Checked exceptions
>>     are equivalent to Either, general Effect system is equivalent to
>>     full monadic type system).
>>
>>     The language is very interesting and worth reading about, but the
>>     relevant part is their Polymorphic Effects, which allow you to
>>     extend the effects. In Java pseudo code, it means that the
>>     following is valid:
>>
>>     |@FunctionalInterface public interface EPredicate<T, E extends
>>     Throwable> { boolean test(T t) throws E; } @FunctionalInterface
>>     public interface EConsumer<T, E extends Throwable> { boolean
>>     test(T t) throws E; } public class EStream<V,E extends Throwable>
>>     { public <EX extends Throwable> EStream<V, (E|EX)>
>>     filter(EPredicate<? super T, (E|EX)> predicate) { ... } public
>>     <EX extends Throwable> void forEach(EConsumer<? super T, (E|EX)>
>>     action) throws E, EX { ... } } |
>>
>>     Which allows you to have a fluent composition without losing the
>>     fine-grained checked exceptions:
>>
>>     |public static void main(string[] args) throws
>>     FileNotFoundException, IllegalAccessException, SQLException {
>>     myEStream.filter(v -> ...) // something that throws
>>     FileNotFoundException .filter(v -> ...) // something that throws
>>     IllegalAccessException .forEach(v -> ...); // something that
>>     throws SQLException } |
>>
>>     This also makes the type system of the |throws| clause a subset
>>     of the generics type system.
>>
>>     While I do believe that the actual solution will require much
>>     more thought than this (e.g. deconstruction of generic sums to be
>>     able to handle specific exceptions and remove those exceptions
>>     from the final union type of exceptions), this is a showcase that
>>     a nice and composable checked exceptions works even without
>>     Monadic types.
>>
>>     I find this direction much nicer than using Either everywhere.
>>
>>     -- 
>>     Holo The Wise Wolf Of Yoitsu
>
>
>
> -- 
> Holo The Wise Wolf Of Yoitsu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230307/0932506f/attachment-0001.htm>


More information about the amber-dev mailing list