6

Definitions used by this question:

Under what scenarios would one style be more beneficial than the other?

Of course, if the called function throws an exception, it's easier to use the 'rx' style. OTOH, it's still possible to wrap that function with another function that follows the 'functional' style.

Noel Yap
  • 171
  • 4
  • Just wait until gnat appears and post his link about [why pros and cons make bad questions](http://meta.programmers.stackexchange.com/questions/6758/what-is-the-problem-with-pros-and-cons) – Vincent Savard May 11 '16 at 16:23
  • 1
    I would think that the pros and cons are the same as those of throwing exceptions vs. not throwing exceptions. – Robert Harvey May 11 '16 at 16:23
  • @VincentSavard: This question seems sufficiently constrained in scope to be answerable Q&A style. – Robert Harvey May 11 '16 at 16:23
  • @RobertHarvey I think it is as well, I just dislike the title. – Vincent Savard May 11 '16 at 16:24
  • @Noel: According to https://github.com/aol/cyclops/wiki/Try-:-functional-exception-handling-for-Java-8, it doesn't throw exceptions; it wraps error handling in a monad. So neither of your definitions throws exceptions. – Robert Harvey May 11 '16 at 16:25
  • @RobertHarvey, AFAIK, exceptions are thrown by the called function when using 'rx' `Try`. I've amended the question to add that qualification. – Noel Yap May 11 '16 at 17:34
  • 1
    @VincentSavard, I've changed the title. – Noel Yap May 11 '16 at 17:36
  • While the ability for rx to catch specific exceptions is nice, you can still do that with your own code using the other framework, so I don't find the distinction all that remarkable. They're both monads; it's just that rx provides you the additional convenience of handling existing exceptions. – Robert Harvey May 11 '16 at 17:52

2 Answers2

8

Both of those are "functional-style" error handling. The only difference is the latter lets you specify that it will handle certain exceptions functional-style while leaving the rest to be handled imperative-style, while the former handles all exceptions functional-style, forcing the programmer to rethrow any unhandled exceptions manually.

The main drawback of imperative-style exception handling is that exceptions only propagate one way: straight up the call stack. You have either the option to handle it there in the call stack or let it propagate. It also forces certain variable scoping choices on you. That is very limiting semantics.

In contrast, Trys are very flexible. They are regular objects that can be stored, shared, aggregated, chained, and handled wherever you like, not just wherever the call stack happened to be when the error occurred. It can take a while to see the usefulness of this.

One recent example from work was we had a list of servers, and wanted to find the first one with a working connection. The API we were working with threw an exception upon an attempt to connect to a down server. The developer who wrote the first draft of this code had a lot of problems with boundary conditions and variable scope due to using try-catch blocks, and asked for help. Trys made the task very simple by lazily mapping the list of servers to a list of Trys, then finding the first Success.

I encourage you to give these libraries a try (pun intended) and see how they can simplify complex error handling. Since learning about Trys, I haven't seen a case yet that they haven't simplified.

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
5

I'm the author of the cyclops Try implementation, so I might be able to shed some light on the design decisions behind it.

To start with the implementations have many similarities, in that you can construct Success and Failure types directly, or execute a Supplier and choose to handle any Exceptions that may be thrown by it.

The goal of Cyclops Try isn't to encourage a mixed approach of imperative and functional exception handling. If you are using cyclops Try all Exception handling using it should be functional. But it should also be premeditated.

By premeditated, I mean that at design time you should bound the scope of what exceptional states you expect your code may get into and configure Try to catch those related exceptions only.

The reason for this is simple, any Exception type that you didn't plan for is a bug and we should fail fast in that case. You want to catch bugs while unit testing, or in your test environments ideally. Not in production, and I don't think you want to mask them permanently in production.

You can tell cyclops Try to catch all Exceptions (e.g. by passing Throwable) and the behaviour would then be the same lambdista Try.

Another difference is that the map and flatMap methods don't handle any subsequent Exceptions by default. You can configure this so that subsequent Exceptions of the specified types by creating a Success Object directly. The thinking here was also to make sure that any Exception handling was deliberate and didn't hide errors (as opposed to encouraging developers to make use of a Try / Catch block elsewhere -we'd rather you didn't).

John McClean
  • 151
  • 4