20

After reading this post on how to use pattern matching on Vector (or any collection that implements Seq), I tested pattern matching on this collection.

scala> x // Vector
res38: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)

scala> x match {
     |    case y +: ys => println("y: " + "ys: " + ys)
     |    case Nil => println("empty vector")
     | }
<console>:12: error: pattern type is incompatible with expected type;
 found   : scala.collection.immutable.Nil.type
 required: scala.collection.immutable.Vector[Int]
Note: if you intended to match against the class, try `case _: <none>`
                 case Nil => println("empty vector")
                      ^

Here's dhg's answer that explains +::

object +: {
  def unapply[T](s: Seq[T]) =
    s.headOption.map(head => (head, s.tail))
}

REPL shows me that

scala> Vector[Int]() == Nil
res37: Boolean = true

... so why can I not use this case Nil statement for an Vector?

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

1 Answers1

33

The comparison Vector[Int]() == Nil is possible because there is no constraint on the type level for what you compare; that allows the implementation of equals for collections, on the other hand, to perform an element by element comparison irrespective of the collection type:

Vector(1, 2, 3) == List(1, 2, 3)  // true!

In pattern matching, you cannot have a case for an empty list (Nil) when the type is not related to list (it's a Vector).

You can do this however:

val x = Vector(1, 2, 3)

x match {
  case y +: ys => println("head: " + y + "; tail: " + ys)
  case IndexedSeq() => println("empty vector")
}

But I would suggest just to use the default case here, because if x does not have a head element, it must be technically empty:

x match {
  case y +: ys => println("head: " + y + "; tail: " + ys)
  case _ => println("empty vector")
}
0__
  • 66,707
  • 21
  • 171
  • 266
  • Thank you for this answer. As an aside follow-up, is using a "catch-all" `_` in pattern matching typically a good or bad practice? I understand your reason for using `_` here since, in your words, "But I would suggest just to use the default case here, because if x does not have a head element, it must be technically empty." – Kevin Meredith Nov 03 '13 at 19:59
  • 3
    Catch all can accidentally capture more cases than you thought. For clearly enumerated (sealed) types I would suggest not to use `_`. For example, with `List` you do have compiler check: `def foo(xs: List[Any]) = xs match { case head :: tail => "yes" }` gives you warning, `def foo(xs: Vector[Any]) = xs match { case head +: tail => "yes" }` doesn't. For `List` I would use `case Nil`, for indexed seq `case _`... – 0__ Nov 04 '13 at 10:31
  • Would using the default case to catch the empty `Vector` as suggested in this answer prevent having the other case make an optimized tail-recursive call? – Adam Mackler Mar 10 '23 at 23:11