How to convert Option[Try[_]] to Try[Option[_]]?
Asked Answered
A

3

7

I quite often use the function below to convert Option[Try[_]] to Try[Option[_]] but it feels wrong. Can be such a functionality expressed in more idiomatic way?

def swap[T](optTry: Option[Try[T]]): Try[Option[T]] = {
  optTry match {
    case Some(Success(t)) => Success(Some(t))
    case Some(Failure(e)) => Failure(e)
    case None => Success(None)
  }
}

Say I have two values:

val v1: Int = ???
val v2: Option[Int] = ???

I want to make an operation op (which can fail) on these values and pass that to function f below.

def op(x: Int): Try[String]
def f(x: String, y: Option[String]): Unit

I typically use for comprehension for readability:

for {
  opedV1 <- op(v1)
  opedV2 <- swap(v2.map(op))
} f(opedV1, opedV2)

PS. I'd like to avoid some heavy stuff like scalaz.

Airspace answered 18/4, 2016 at 16:49 Comment(2)
The definition of swap looks reasonable. I'm not sure what more you could want from it.Manducate
One thought: this operation is usually (idiomatically?) called sequence rather than swap.Breazeale
F
4

Sounds like Try { option.map(_.get) } will do what you want.

Freitas answered 18/4, 2016 at 18:26 Comment(6)
In my view throwing and catching the exception like this has enough overhead (e.g. at runtime but more importantly for readers) that the OPs implementation is preferable, even if it's a little less concise.Lonna
What is the overhead of throwing exception (beyond instantiating it and filling in the stack, which would have already been done by now)? I am not aware of it being significantly larger than calling a function. In any event, I figure, if one is concerned with overhead of throwing exceptions, he should not be using Try to begin with. Or scala for that matter (there are things in scala that pose a lot more overhead and are a lot harder to avoid than exceptions). Just stick with "C" and gotos to be sure to minimize your overhead :)Freitas
That's why I emphasized the overhead for human readers. I often grep for .get calls on Try or Option as a quick measure of the health of a project that's new to me, and I don't make exceptions because the authors are being clever in order to save a couple of lines and avoid a pattern match. :)Lonna
Well, that's too bad. I always make exceptions for clever people. I find that they are hard enough to come by to deserve to be treated differently than the stupid ones every now and again ;)Freitas
Agreed with @Dima. Exception overhead is second to none, comparing to other stuff. What I've learned so far is just: most of the time don't use util.Try; it doesn't play nicely with standard library.Airspace
@Freitas If Josh Bloch's micro benchmark from Effective Java is anything to go by, I'd expect that throwing an exception only to catch immediately is significantly less performant than returning it as a value. Regardless of performance, in an expression-oriented language like Scala, manipulating values rather than throwing exceptions is considered more idiomatic. I'm with Travis on this one.Sheliasheline
S
8

The cats library allows you to sequence an Option to a Try very easily:

scala> import cats.implicits._
import cats.implicits._

scala> import scala.util.{Failure, Success, Try}
import scala.util.{Failure, Success, Try}

scala> Option(Success(1)).sequence[Try, Int]
res0: scala.util.Try[Option[Int]] = Success(Some(1))

scala> Option(Failure[Int](new IllegalArgumentException("nonpositive integer"))).sequence[Try, Int]
res1: scala.util.Try[Option[Int]] = Failure(java.lang.IllegalArgumentException: nonpositive integer)

scala> None.sequence[Try, Int]
res2: scala.util.Try[Option[Int]] = Success(None)
Sheliasheline answered 3/11, 2016 at 14:28 Comment(0)
F
4

Sounds like Try { option.map(_.get) } will do what you want.

Freitas answered 18/4, 2016 at 18:26 Comment(6)
In my view throwing and catching the exception like this has enough overhead (e.g. at runtime but more importantly for readers) that the OPs implementation is preferable, even if it's a little less concise.Lonna
What is the overhead of throwing exception (beyond instantiating it and filling in the stack, which would have already been done by now)? I am not aware of it being significantly larger than calling a function. In any event, I figure, if one is concerned with overhead of throwing exceptions, he should not be using Try to begin with. Or scala for that matter (there are things in scala that pose a lot more overhead and are a lot harder to avoid than exceptions). Just stick with "C" and gotos to be sure to minimize your overhead :)Freitas
That's why I emphasized the overhead for human readers. I often grep for .get calls on Try or Option as a quick measure of the health of a project that's new to me, and I don't make exceptions because the authors are being clever in order to save a couple of lines and avoid a pattern match. :)Lonna
Well, that's too bad. I always make exceptions for clever people. I find that they are hard enough to come by to deserve to be treated differently than the stupid ones every now and again ;)Freitas
Agreed with @Dima. Exception overhead is second to none, comparing to other stuff. What I've learned so far is just: most of the time don't use util.Try; it doesn't play nicely with standard library.Airspace
@Freitas If Josh Bloch's micro benchmark from Effective Java is anything to go by, I'd expect that throwing an exception only to catch immediately is significantly less performant than returning it as a value. Regardless of performance, in an expression-oriented language like Scala, manipulating values rather than throwing exceptions is considered more idiomatic. I'm with Travis on this one.Sheliasheline
U
1

This variant avoids rethrowing:

import scala.util.{Failure, Success, Try}

def swap[T](optTry: Option[Try[T]]): Try[Option[T]] =
  optTry.map(_.map(Some.apply)).getOrElse(Success(None))

swap(Some(Success(1)))
// res0: scala.util.Try[Option[Int]] = Success(Some(1))

swap(Some(Failure(new IllegalStateException("test"))))
// res1: scala.util.Try[Option[Nothing]] = Failure(java.lang.IllegalStateException: test)

swap(None)
// res2: scala.util.Try[Option[Nothing]] = Success(None)
Uncouth answered 18/4, 2016 at 22:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.