The official scala docs say that collect “builds a new collection by applying a partial function to all elements of this sequence on which the function is defined.”
To understand what this means, it’s helpful to see an example of a partial function used in this way. For instance, we can make a function that returns the original value, but handling different input types. This function takes “Any” and turns it into an Int (hence the signature, [Any, Int]).
Even though the function takes an “Any”, it would fail for many types of values; e.g. floating point values – these Scala will drop out of the final collection. This is pretty handy, because you can guarantee that this won’t error out, and you can guarantee that you only get the types of output you want.
val convertFn: PartialFunction[Any, Int] = {
case i: Int => i;
case s: String => s.toInt;
case Some(s: String) => s.toInt
}
List(0, 1, "2", "3", Some(4), Some("5")).
collect(convertFn)
List[Int] = List(0, 1, 2, 3, 5)
By contrast, any value we return that isn’t an Int, String, or Some(String) gets knocked out of the list – essentially this does a “map” and a “filter” at the same time.
scala> List(6.5, None, null, Unit).collect(convertFn)
res93: List[Int] = List()
Note, however that this isn’t something that catches errors, it’s actually interrogating the function to determine whether it is defined at a particular point; if not, the function is never called.
To prove this, you can define a function that is not implemented for anything other than ints, and collect will fail:
List(1, "").collect(
{
case i: Int => i;
case _ => ???
}
)
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
at $anonfun$1.applyOrElse(:8)
at scala.collection.immutable.List.collect(List.scala:303)
... 33 elided
You wrote:
List(0, 1, “2”, “3”, Some(3), Some(“4”)).
collect(convertFn)
List[Int] = List(0, 1, 2, 3, 4, 5)
The result should be:
res0: List[Int] = List(0, 1, 2, 3, 4)
The Some(3) is dropped from the list and there was never an int 5.
Sorry about that! I’ve corrected the post.
Great explanation, thanks for sharing with us.
Thanks for the explanation. I’m not understanding the final part though. Why does NotImplementedError imply that the function is never called?
You’re right. The provided function is in effect not partial but a total function as it accepts any value. The correct form should only accept Int values:
List(1, “”) collect { case i: Int => i }