Checked exceptions within Block<T>

Michael Hixson michael.hixson at gmail.com
Sun Jan 13 01:39:57 PST 2013


Thanks everyone.  It sounds like a combination of Peter and Brian's
suggestions would work best for my case: another class like BlockEx<T>
with a static helper method that wraps a BlockEx in a RTE-throwing
Block.  That would let me keep the checked exceptions in my utility
class with a minimal amount of exception-related boilerplate in my
handler classes, where I know I'm at the top-most level and I happen
to not care whether exceptions are checked anyway.

By the way, I agree that the glass is "99% full" and never suggested
doing away with checked exceptions (under a veil of any thickness).
In my experiments with the lambda builds, this (the difficulties in my
original email) was the most I could complain about.  If the expert
group already through about this use case and decided language-level
changes would be harmful or unnecessary, then... ok! :)  This was
exactly the kind of response I was hoping for.

-Michael

On Sat, Jan 12, 2013 at 10:10 AM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> Cool. I'm getting that you guys have better future plans, so you
> resist stupid short term fixes.
>
> On Sat, Jan 12, 2013 at 11:49 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
>> Yes, you'd have to provide your own exceptional SAMs.  But then lambda
>> conversion would work fine with them.
>>
>> The EG discussed additional language and library support for this problem,
>> and in the end felt that this was a bad cost/benefit tradeoff.
>>
>> Library-based solutions cause a 2x explosion in SAM types (exceptional vs
>> not), which interact badly with existing combinatorial explosions for
>> primitive specialization.
>>
>> The available language-based solutions were losers from a complexity/value
>> tradeoff.  Though there are some alternative solutions we are going to
>> continue to explore -- though clearly not for 8 and probably not for 9
>> either.
>>
>> In the meantime, you have the tools to do what you want.  I get that you
>> prefer we provide that last mile for you (and, secondarily, your request is
>> really a thinly-veiled request for "why don't you just give up on checked
>> exceptions already"), but I think the current state lets you get your job
>> done.
>>
>>
>> On 1/12/2013 12:35 PM, Zhong Yu wrote:
>>>
>>> You are probably mistaken here, since b.accept(e) does not throw
>>> checked exceptions.
>>>
>>> If you what you really meant was a solution like Peter Levart's,
>>> should it (BlockEx) be included in standard lib? Since it might be
>>> useful to a wide audience.
>>>
>>> Zhong Yu
>>>
>>> On Sat, Jan 12, 2013 at 11:21 AM, Brian Goetz <brian.goetz at oracle.com>
>>> wrote:
>>>>
>>>> Or you could write your own trivial combinator:
>>>>
>>>> static<T> Block<T> exceptionWrappingBlock(Block<T> b) {
>>>>      return e -> {
>>>>          try { b.accept(e); }
>>>>          catch (Exception e) { throw new RTE(e); }
>>>>      };
>>>> }
>>>>
>>>> You can write it once, in less that the time it took to write your
>>>> original
>>>> e-mail.  And similarly once for each kind of SAM you use.
>>>>
>>>> I'd rather we look at this as "glass 99% full" rather than the
>>>> alternative.
>>>> Not all problems require new language features as solutions.  (Not to
>>>> mention that new language features always causes new problems.)
>>>>
>>>>
>>>>
>>>> On 1/12/2013 11:03 AM, Zhong Yu wrote:
>>>>>
>>>>>
>>>>> I too have this problem, since we have lots of code throwing checked
>>>>> exceptions, it's a challenge to wrap them in functional interfaces
>>>>> that do not throw.
>>>>>
>>>>> If the solution is to smuggle checked exception as unchecked, JDK
>>>>> should provide a standard class for that specific purpose, or
>>>>> everybody will be forced to invent their own.
>>>>>
>>>>> A better solution is probably having varying exceptions
>>>>>
>>>>>       interface Block<T, E extends Throwable>
>>>>>           void apply(T input) throws E;
>>>>>
>>>>>       <T,E extends Throwable> void forEach(Block<T,E> block) throws E
>>>>>
>>>>> this is very ugly though; I'd dream the language could make it simpler
>>>>> like
>>>>>
>>>>>       interface Block<T>
>>>>>           void apply(T input) throws ?;
>>>>>           // abstract method; throws E; add E to interface.
>>>>>
>>>>>       <T> void forEach(Block<T> block) throws ? { ... }
>>>>>       // non-abstract method; throws E; add E to method.
>>>>>
>>>>>
>>>>> Zhong Yu
>>>>>
>>>>> On Fri, Jan 11, 2013 at 10:12 PM, Michael Hixson
>>>>> <michael.hixson at gmail.com> wrote:
>>>>>>
>>>>>>
>>>>>> This is a bit of feedback for the lambda snapshots.  It's not really
>>>>>> suggesting any changes or reporting bugs, but rather describing
>>>>>> difficulties I had.  Hopefully this is the right mailing list for this
>>>>>> sort of thing.
>>>>>>
>>>>>> --------------------------------
>>>>>>
>>>>>> An issue that came up repeatedly was that I wanted refactor code like
>>>>>> this:
>>>>>>
>>>>>>     for (Value value : values) {
>>>>>>       ...
>>>>>>     }
>>>>>>
>>>>>> Into this:
>>>>>>
>>>>>>     values.forEach(value -> {
>>>>>>       ...
>>>>>>     });
>>>>>>
>>>>>> But I couldn't because the "..." could throw a checked exception.  I
>>>>>> worked around this in two ways:
>>>>>>
>>>>>>     (a) Don't refactor the code.
>>>>>>     (b) Change whatever is throwing the checked exception to throw a
>>>>>> runtime exception instead.
>>>>>>
>>>>>> Option (a) was not so bad if my "values" object was an Iterable.  It
>>>>>> was worse when my values were a Map, a Stream, or an Optional.  To
>>>>>> compare:
>>>>>>
>>>>>>     for (Map.Entry<Key, Value> entry : values.entrySet()) {
>>>>>>       Key key = entry.getKey();
>>>>>>       Value value = entry.getValue();
>>>>>>       ...
>>>>>>     }
>>>>>>
>>>>>>     values.forEach((key, value) -> {
>>>>>>       ...
>>>>>>     });
>>>>>>
>>>>>>     for (Iterator iterator =
>>>>>> values.stream().filter(predicate).map(function).iterator();
>>>>>>          iterator.hasNext();) {
>>>>>>       Value value = iterator.next();
>>>>>>       ...
>>>>>>     }
>>>>>>
>>>>>>     values.stream().filter(predicate).map(function).forEach(value -> {
>>>>>>       ...
>>>>>>     });
>>>>>>
>>>>>>     Optional<Value> optionalValue = somethingThatGivesAnOptional();
>>>>>>     if (optionalValue.isPresent()) {
>>>>>>       Value value = optionalValue.get();
>>>>>>       ...
>>>>>>     }
>>>>>>
>>>>>>     somethingThatGivesAnOptional().ifPresent(value -> {
>>>>>>       ...
>>>>>>     });
>>>>>>
>>>>>> In one case (really several cases that relied on the same utility)
>>>>>> that was enough to drive me to option (b).
>>>>>>
>>>>>> Option (b) allowed me to use the new lambda goodness but made me
>>>>>> slightly uncomfortable.  The utility in question could write values in
>>>>>> CSV format to an Appendable.  Since Appendable can throw IOExceptions,
>>>>>> I initially had the utility methods throw IOException.  To make it
>>>>>> lambda-friendly I changed all its methods to look like this:
>>>>>>
>>>>>>     try {
>>>>>>       ...
>>>>>>     } catch (IOException e) {
>>>>>>       throw new UncheckedIOException(e);
>>>>>>     }
>>>>>>
>>>>>> This was fine because I wasn't catching any IOExceptions that occurred
>>>>>> in the first place (this code was running in response to a web
>>>>>> request, and if an exception occurred the framework would handle it
>>>>>> and show an error page).  But I felt that it made my CSV utility a
>>>>>> little more "dangerous" for general use.  This is all sort of
>>>>>> hand-wavey, but I don't like that the utility now potentially throws
>>>>>> runtime exceptions when there's not a programmer error and when there
>>>>>> might be a reasonable way to recover.  Plus it is additional code in
>>>>>> my utility class, and good thing it is my class and not someone
>>>>>> else's.
>>>>>>
>>>>>> What I really wanted to do was leave in the checked exceptions *and*
>>>>>> use the lambda forEach/ifPresent forms.
>>>>>>
>>>>>> -Michael
>>>>>>
>>>>>
>>>>
>>


More information about the lambda-dev mailing list