Search code examples
scalafor-comprehensionextractor

Why does a for-comprehension used with an extractor of type tuple result in a compile warning on `filter`?


Given the following code snippelt:

import scala.util.Try

def foo(x:Int) : (Int, String) = {
  (x+1, x.toString)
}

def main(args: Array[String]) : Unit = {
  val r1: Try[(Int, String)] = for {
    v <- Try { foo(3) }
  } yield v

  val r2: Try[(Int, String)] = for {
    (i, s)  <- Try { foo(3) } // compile warning refers to this line
  } yield (i, s)
}

1. Why does a compile of the above code throw the following warning?

`withFilter' method does not yet exist on scala.util.Try[(Int, String)], using `filter' method instead
[warn]       (i, s)  <- Try { foo(3) }
[warn]                      ^
[warn] one warning found

2. Why is withFilter used at all when extracting to a tuple?

Update

  • The warning occurs with Scala 2.10.5
  • The warning does not occur with Scala 2.11.7

Independent of the warning message, I'm heavily interested to know if withFilter is used? (see question 2 )


Solution

  • It seems Try.withFilter was only added in 2.11 (see Try 2.10.6 and Try 2.11.0)

    withFilter is used instead of filter in a for comprehension because it is lazy, you can read a more comprehensive comparison in this question.

    Your second for comprehension is similar to :

    Try(foo(3)).withFilter {
      case (i, s) => true
      case _      => false
    } map(identity)
    

    Because in Scala 2.10.5 Try.withFilter doesn't exists, it falls back to using filter (which creates a new Try).


    Edit: Why you need the withFilter is not that obvious in your case, because you aren't actually filtering with the (i, s) pattern match.

    If you wrote the for comprehension below, it is more clear that you (can) filter when you add a pattern match in the left hand side of a for comprehension.

    for {
      (i, "3") <- Try(foo(3))
    } yield i
    

    Which is analogous to :

    Try(foo(3)) withFilter {
      case (i, "3") => true
      case _        => false
    } map { case (i, "3") => i }
    

    As you can see withFilter is not only used when you add an if guard, but also when you pattern match.