Scala WTF 1

So here is a puzzle. What will be written by this program:

object Wtf1 {
  def main(args: Array[String]): Unit = {
    val points: List[Point] = List(
      Point(1, 2),
      Point(3, 4),
      Point(5, 6)
    )

    val result = points.contains(Point(3, _))
    println(s"result: $result")
  }
}

case class Point(x: Int, y: Int)

// ANSWER BELOW
// ..............................................
//

The answer is false. Why? Because the line with points.contains(Point(3, _)) instead of performing pattern matching, checks whether points contain a function:

val result = points.contains(Point(3, _))
// in reality is:
val result = names.contains((n: Int) => Point(3, n))

The strangest thing for me is that the compilation of this code does not generate any warnings. From Scala compiler point of view the above code is perfectly valid and this in turn is the result of List type being covariant. Or in the other words because we can assign List[Point] to List[Any], contains must accept arguments of any type:

val points: List[Point] = List(
    Point(1, 2),
    Point(3, 4),
    Point(5, 6)
)

val anys: List[Any] = points

anys.contains(new Object())

The declaration of contains method in List[A] looks like this:

def contains[A1 >: A](elem: A1): Boolean

We may snoop the actual types assigned to the generic parameters using a helper method:

def detectType[A, A1 >: A](l: List[A], obj: A1)
                          (implicit tagA: ClassTag[A], tagA1: ClassTag[A1]): Unit = {
    println(s"type of A : ${tagA.runtimeClass.getName}")
    println(s"type of A1: ${tagA1.runtimeClass.getName}")
}

detectType(names, Point(3, _))

// This prints:
// type of A : Point
// type of A1: java.lang.Object
//

So during the compilation A1 becomes Object and everything type-checks.

Let’s finish by writing a code that actually does what the programmer intended:

val result = names
    .collectFirst { case Point(3, _) => true }
    .getOrElse(false)