Search code examples
scalapartialfunction

Why does providing a partial function to map throw at runtime rather than give a compile error?


Apologies if this is obvious but I am new to scala and I am getting two unexpected behaviors with the following code:

Seq(1, "a", 2, "b") map { 
    case i: Int => i+1
}

1) I would have expected to get back a collection where the strings are unchanged and the numbers are incremented by 1 but instead I get an error.

2) I believe the case i: Int => i + 1 syntax represents a partial function which is defined for Ints. But it seems map takes a total function, so why does this even compile? Wouldn't it be better for the compiler to help me out? It is always better to move runtime exceptions to compile time exceptions.


Solution

  • map() does not take a partial function as the passed parameter, but collect() does.

    Seq(1, "a", 2, "b") collect {
      case i: Int => i+1
    }
    //res0: Seq[Int] = List(2, 3)
    

    Notice how input that is not defined for the partial function is not passed through but is simply dropped. What you don't want dropped needs a handler, even if it's just a case _ => default handler.

    Seq(1, "a", 2, "b", 'z') collect {
      case i: Int    => i+1        //increment ints
      case c: Char   => c.toUpper  //capitalize chars
      case s: String => s          //strings pass through
    }
    //res0: Seq[Any] = List(2, a, 3, b, Z)
    

    When you pass a partial function to map() the compiler does not complain because trait PartialFunction[-A, +B] extends (A) => B. In other words, a partial function is a type of function.

    It's also worth noting that, when dealing with partial functions...

    It is the responsibility of the caller to call isDefinedAt before calling apply...

    So we can conclude that collect() does that and map() doesn't.