- edited description
Add Throwables.sneakyThrow
Sometimes it is necessary to throw a checked exception, but the enclosing method has no throws
keyword. Example:
FluentIterable.from(Collections.list(NetworkInterface.getNetworkInterfaces())).filter(
new Predicate<NetworkInterface>() {
@Override public boolean apply(NetworkInterface iface) {
return !iface.isLoopback();
}
});
Since Predicate.apply
has no declared Exception that can be thrown, this code does not compile. I could use Throwables.propagate
, but I don't want to wrap SocketException into RuntimeException.
This is a severe issue because I cannot use filter()
anymore and instead I have to use an imperative loop.
The solution is to add a method that can sneaky throw any Throwable even if it is checked:
public static RuntimeException sneakyThrow(Throwable t) {
throw Throwables.<RuntimeException> sneakyThrowInner(t);
}
private static <T extends Throwable> T sneakyThrowInner(Throwable t) throws T {
throw (T) t;
}
Then I can handle a SocketException using sneakyThrow
:
try {
return !iface.isLoopback();
} catch (SocketException ex) {
throw sneakyThrow(ex);
}
Also, see the original discussion in Guava issues: https://github.com/google/guava/issues/626
Comments (20)
-
reporter -
reporter - edited description
-
Hi @orionll,
Can you expand on how you want to treat the exception case differently than just straight filtering? A valid solution here would be to just return false if an exception were thrown. You probably don't want to do that so please expand a little on you example.
Alternatively this problem can also be solved by using the Eithers#sequence function.
Either<SocketException, List<NetworkInterface>> networkInterfaces; try { networkInterfaces = right(Collections.list(NetworkInterface.getNetworkInterfaces())); } catch (SocketException s) { networkInterfaces = left(s); } Either<SocketException, Iterable<NetworkInterface>> sequenced = networkInterfaces.flatMap(ifaces -> { Iterable<Either<SocketException, NetworkInterface>> mapped = Iterables.collect(ifaces, iface -> { try { if (iface.isLoopback()) { return some(right(iface)); } else { return none(); } } catch (SocketException s) { return some(left(s)); } }); return Eithers.sequenceRight(mapped); }); // if sequenced is a Right all of the network interfaces returned a result from isLoopback // if sequenced is a Left it's the first exception that was thrown.
You can simplify the boiler plate of the first try block and maybe the second. Note that this solution obscures the point where the SocketExcpetion was thrown. A more complete solution would use an error type on the left to distinguish where exactly the exception was generated.
-
fugue is dedicated to providing alternatives to side-effects, and in particular a useful Either that can represent the disjunction of an error or a result.
This is not something we would want to encourage although we should provide more help for sensible alternatives such as what @amckague is proposing.
-
fugue is dedicated to providing alternatives to side-effects, and in particular a useful Either that can represent the disjunction of an error or a result.
This is not something we would want to encourage although we should provide more help for sensible alternatives such as what @amckague is proposing.
-
@orionll there's room for something like:
<A, E extends Exception> Eithers.catching(Suppiler<A>): Either<E,A> // and/or <A, B, E extends Exception> Eithers.catching(Suppiler<A>, Function<E,B>): Either<B,A>
to help reduce the number of times you'd need to try/catch things. Is there other work that could be done from this issue?
-
reporter Using Either is not always possible. For example, what to do if I want to throw SQLException inside Effect.apply?
-
If you want to throw exceptions, and then handle the result, then Effect isn't the thing you want. You should use map and return
Either<SQLException, A>
. You can then rethrow that SQLException from a method that declares is if you really want to (not a recommendation). -
reporter What if I have no map? My structure is not a functor. It is not generic at all: DoubleArray implements Applicant<Double>. How would you rewrite this code with Either?
DoubleArray array = ... createSQLTable(); array.foreach(d -> fillSQLTable(d));
-
Then I am missing the justification for why this should be added to fugue, and not to some other library. This is quite orthogonal to the objectives of this library, which is to provide useful foundations for functional programming in Java.
-
reporter So why does Throwables class exist at all?
-
Very good question. The actual reason it was added is lost to me now, I certainly remember not particularly liking it, but there being a strong internal campaign for it. I acquiesced, but still see it as a wart in the library.
IIRC it was something about making the interface between pure code and imperative code easier, and was pitched as a convenience that would help increase the rate of adoption of functional code. I am skeptical that it really achieves this result however.
edit it is hard to get rid of code once introduced. I'd love to remove
Throwables
and note that it is deprecated in fuge 3.0. -
Throwables is deprecated and has been removed from fugue 3... I also use sneaky throw for one or two use case in our code base - to please a framework I had to use - but I would rather have it use not be widespread; it does break java type system: you cannot catch a checked exception that have been sneaky thrown. (without resorting to a stub method that threats to throw that exception without actually doing anything)
-
I think this issue has finished. If no one objects I'll close this out tomorrow.
-
reporter Close it if you want, but I still do not agree
-
We can agree the intersection of functional interfaces and checked exceptions is not pleasant. We can also agree that you are not always able to express your program in terms of an Either. You are correct this solves the problem presented by checked exceptions without wrapping and rethrowing. It is more succinct. It does not avoid the need to cast the thrown exception back to a useful type (for checked exceptions). The move to deprecate Throwables means sneakyThrow no longer fits in this library.
Where you find it necessary for sneakyThrow it is a small enough snippet of code to just copy it around. A memorable tool that can be applied at need.
-
- changed status to resolved
Throwables is deprecated in 3.0 and will be removed in 4.0
-
I don't see Throwables at all in m006.
-
It's been moved to: https://maven.atlassian.com/repository/internal/com/atlassian/fugue/fugue-guava/3.0.0-m006/fugue-guava-3.0.0-m006.jar Quick warning, it's missing an osgi manifest and the code you're looking for has moved packages to avoid osgi split packages problems. Have a read of the most recent 3.0 change log https://bitbucket.org/atlassian/fugue/src/9877c33c483bd4769b6b3dee3e73f9a3cacbf49f/changelog.md?at=v3.0.x
The move to remove it in 3.0 would probably come too soon given that the deprecation was introduced and released at roughly the same point in time.
At the end of this there will be an artefact in maven central that easier to find things for. The internal proxy is making this harder than it needs to be.
-
No guarantees on the artifactid staying fugue-guava before the release is cut.
- Log in to comment