Using `Try` as return type of a function

As much as I understand we should use Try as a result type of a function if we want to indicate that the errors might occur when invoking it.

Now my question: What is the idiomatic way to do that in Scala?

A)

def check(value: String): Try[String] = {
  value match {
    case "Foo" => Success("Foo")
    case "Bar" => Success("Bar")
    case value => Failure(new UnsupportedValueException(value))
  }
}

or
B)

def check(value: String): Try[String] = {
  Try {
    value match {
      case "Foo" => "Foo"
      case "Bar" => "Bar"
      case value => throw new UnsupportedValueException(value)
    }
  }
}

I would go with the first one, and probably would use an Either instead of Try.

The reason is that, IMHO, the apply Try is very useful when you are dealing with legacy code that already throws exceptions. But, for new codebases, it makes sense to avoid throwing since a beginning and treat errors as values.

Now the Either is most of a personal preference and that it is more general. For example, errors do not have to be Throwables.

1 Like

Seconded.

…and for interfacing with the occasional exception-based API, there’s Try#toEither.

And you can limit the left case to a specific type with Either - you can’t have a Try for a subtype of Throwable.

2 Likes

And what Type would you use for the Left? A new error class for every possible error? That sounds to me a little “over the top”.

Note that you can still throw exceptions and annotate the function with @throws. That’s totally fine, but you don’t get the compiler checking from the compiler like you do in Java.

Read more about this here.

I just do not want to use Exceptions in a functional implementation.

Uhm, that really depends on how you design all your program with respect to error handling. As I said, the use of Either is more of a style and personal choice.

I do not want this to convert into a extensive and somewhat off-topic discussion. But just for answering your question, here are a couple of options:

  • Just use Throwable and it would be exactly the same as the Try.
  • Since the only possible failure is UnsupportedValueException you can use that, do not worry if it is too specific, once you start composing values you would end with a more general type, probably Throwable again.
  • You can create your own hierarchy of errors if you want to capture a small group of things that may go wrong and that you (or the users of the code) know how to deal which each of them.
  • You may just use Strings if at the end, you only want / need / can log them.

It really depends on how you want to manage your errors. Anyways, let me express myself again, nothing wrong with using Try, it is very powerful. It is juts that IMHO once you are not longer interacting with code that throws exceptions everywhere and all your businesses errors are values (maybe Throwables) that I feel like Either is a better choice.

1 Like

If you wish to choose a programming style just for the sake of using that style, then by all means; however, it’s worth being aware of other styles and be able to evaluate the pros and cons of each of them.

Similar to Exceptions: A type hierarchy that’s tailored to the semantics of the specific API.

pureconfig, for example, uses a ConfigReaderFailure hierarchy.

1 Like

I created my own class for errors to be in the Left of Either, which I’m now using across projects. I called it Snag, because I felt the term Error is already overused and would easily lead to ambiguities. A Snag has a short message and a report, which may be the message or optionally something longer. It optionally has another Snag as a cause, similar to Throwables. I have a convenience method to convert a Throwable into a Snag, with the Throwable message becoming the Snag message, the stack trace being written into the report and the cause, if there is one, being likewise converted from Throwable to Snag.

I thought about having different types of Snags, but so far found no benefit, since all Snags end up being reported to the user anyway. Also, if the need for different Snags may arise, I probably would rather add an error code as an extra field instead of creating different types.

1 Like