Abhijit Sarkar Abhijit Sarkar - 25 days ago 10
Scala Question

Scala Try and Either Weird Behavior

So I am playing with the

M
types in Scala and came up with the following regarding
Try
and
Either
:

def brokers(throw1: () => List[Int], throw2: List[Int] => List[String]) = {
println("brokers ===> " +
(Try(throw1())
.toEither
.filterOrElse(!_.isEmpty, Nil)
.flatMap(xs => Try(throw2(xs)).toEither) match {
case Right(s) => s
case Left(f) => throw f.asInstanceOf[Throwable]
})
)
}


And some test runs:

brokers(() => List(1, 2, 3), (xs: List[Int]) => xs.map(_.toString))
brokers(() => Nil, (xs: List[Int]) => throw new RuntimeException("throw2"))
brokers(() => Nil, (xs: List[Int]) => xs.map(_.toString))
brokers(() => throw new RuntimeException("throw1"), (xs: List[Int]) => xs.map(_.toString))
brokers(() => List(1, 2, 3), (xs: List[Int]) => throw new RuntimeException("throw2"))


But:


  1. throw f
    wouldn't compile: "Expression of type Serializable doesn't conform to expected type Throwable". Thus the cast.

  2. Exception from sample run 2:




Exception in thread "main"
java.lang.ClassCastException: scala.collection.immutable.Nil$ cannot
be cast to java.lang.Throwable at
Practice$.brokers(Practice.scala:57) at
Practice$.delayedEndpoint$Practice$1(Practice.scala:63)


Why, Scala, why?

Answer

Answering my own question, turns out Either.filterOrElse is not really what I thought it to be. If the predicate doesn't match, filterOrElse actually converts Either[A, B] to Either[AA, B] where AA is the "zero" element provided to filterOrElse. In my case, it converted the Either[Throwable,List[Int]] = Right(List()) to a Either[java.io.Serializable,List[Int]] = Left(List()). Thus the compile error, and the match with case Left and eventual exception. The Serializable must have come from the contravariant type parameter AA >: A, because guess what, the first common supertype for Throwable and List is a Serializable.

I've filed SI-10044 for this; will see what they say.