A pattern that that I see with some frequency in Scala is "Try unwrapping" which usually looks like this:
Try {
input.charAt(100) // Some error-prone calculation
} match {
case Success(c) => c // just unwrap the value
case Failure(e) =>
println(e) // log the exception
'?' // and provide a default value
}
This code is fine, but I'm a little bothered by the case Success(c) => c, which is basically just an identity transformation (a.k.a. noise). The soul of the transformation is the error mapping function that logs the exception and provides a default value, and that's what I want to bring to the foreground.
I can see two alternatives that get rid of the identity transform:
We could use the recover method:
Try {
input.charAt(100) // Some error-prone calculation
} recover { e =>
println(e) // log the exception
'?' // and provide a default value
} get
but recover produces a Try[T] (which is guaranteed to be a Success). That has to be unwrapped with a dangling get (which also needs need postFixOps in this style).
We could also use getOrElse, but this doesn't allow us to log the exception.
Try {
"input".charAt(100) // Some error-prone calculation
} getOrElse {
// println(e) // We don't have access to the exception and can't log it
'a' // provide a default value.
}
What I would really like is an unwrap or recover++ method like
def unwrap[U >: T](pf: PartialFunction[Throwable, U]): U
that either unwraps the T from the Success or unwraps the Throwable from failure and transforms it to a U with the partial function.
The easiest way I can see to do that would be to wrap the recover/get pipeline in an extension method that I add to Try, but that also sounds like overkill.
Does anyone have a slick way to implement this behavior?
P.S. I found this related question that suggests that leans towards pattern matching, but I want to see if anything better has come around in the last 5 years - Try recover get vs Try match
I've used the recover pattern to catch and log errors. You can also just re-throw the exception (or a different exception) from the recover block, like in this simple Json parsing code:
Try {
(parse(json) \ "something").extractOrElse("default")
}.recover({
case exception: Exception => throw new IllegalArgumentException(s"Invalid JSON: ${exception.getMessage}")
}).get
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With