1

Why it's impossible to compile this code

IndexedSeq(1, 2, 3) match {
  case a :: b :: c => println("toto");
}

But it's possible with a Seq ?

IndexedSeq(1, 2, 3).toSeq match {
  case a :: b :: c => println("toto");
}

The IndexedSeq.toSeq method is just a cast !

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Philippe Prados
  • 461
  • 3
  • 12

3 Answers3

4

I'm not sure why the second one compiles, but I do know that it doesn't work!

scala> IndexedSeq(1,2, 3).toSeq match {
  case a :: b :: c :: nil => println("toto");
}
     |      | scala.MatchError: Vector(1, 2, 3) (of class scala.collection.immutable.Vector)

If you want to pattern match a sequence, you either need to use +: as the joining operator, or use Seq(a,b,c) as the pattern to match. See this answer

The following all work as desired:

IndexedSeq(1,2, 3).toSeq match {
  case Seq(a, b, c) => println("toto");
}

IndexedSeq(1,2, 3) match {
  case Seq(a, b, c) => println("toto");
}

IndexedSeq(1,2, 3).toSeq match {
  case a +: b +: c  => println("toto");
}

IndexedSeq(1,2, 3) match {
  case a +: b +: c  => println("toto");
}
Community
  • 1
  • 1
Iadams
  • 536
  • 3
  • 7
1

To add to @ladams 's answer. Calling toSeq on IndexedSeq is just referring to the super as the latter inherits from the former. On the side note, you could also extract the values the following way:

  IndexedSeq(1, 2, 3) match {
    case IndexedSeq(head, tail @ _*) => println("got match")
    case _ => println("sth else")
  }

Also, Seq is just a trait that returns appropriate collection, e.g.

val s: Seq[Int] = 1 :: 2 :: 3 :: Nil
s: Seq[Int] = List(1, 2, 3)

Therefore, you can match it with Cons. Whereas IndexedSeq returns a Vector, that does not define a Cons operator.

val s: IndexedSeq[Int] = IndexedSeq(1, 2, 3)
s: IndexedSequence[Int] = Vector(1, 2, 3)
sebszyller
  • 853
  • 4
  • 10
  • *Seq is just a trait that returns appropriate collection* What about it being a `trait` makes it be able to match on `Cons`? `IndexedSeq[A]` is also a `trait`. – Yuval Itzchakov Jul 06 '16 at 08:08
  • It is not its being a trait but a fact that calling Seq(1, 2, 3) returns a List(1, 2, 3) that implements a Cons. – sebszyller Jul 06 '16 at 08:13
  • But `IndexedSeq(1,2,3).toSeq` returns a `Vector[Int]`. – Yuval Itzchakov Jul 06 '16 at 08:14
  • You are correct. Though the thing is that this: val i = IndexedSeq(1, 2, 3) val j = i.toSeq j match { case a :: b :: c => println("fine") case _ => println("not fine") } compiles but goes to "not fine". It appears that at compile time it infers the provided types but at runtime it is not able decouple the Vector with Cons (obviously) and goes to the wildcard. Side note, how to post code in comments? – sebszyller Jul 06 '16 at 08:29
  • But that's exactly what this question is about. Why does it work? :) – Yuval Itzchakov Jul 06 '16 at 08:30
  • Updated comment with explanation. – sebszyller Jul 06 '16 at 08:49
  • "that does not define a Cons operator." Pattern matching does not use the Cons operator, but unapply. So this explanation isn't really correct – The Archetypal Paul Jul 06 '16 at 09:12
  • @3jckd I think I have a ball park explanation of what happens here. See my answer. – Yuval Itzchakov Jul 06 '16 at 09:18
  • Of course it uses an extractor for reverse contruction; it was just a mental shortcut. @Yuval Itzchakov, I cannot comment on your post. Good job on that one - paraphrase of my comment with nice explanation. – sebszyller Jul 06 '16 at 10:19
1

The first example doesn't compile because the type of the pattern a :: b :: c is List[something] and the type of the selector IndexedSeq(1, 2, 3) is IndexedSeq[Int]. Because List isn't a subtype of IndexedSeq, the compiler knows that the match can't possibly succeed and produces an error. As described in the language specification (rephrased slightly to remove the formulas):

Every pattern can be typed in two ways. First, it is attempted to type the pattern with the selector's type as its expected type. If this fails, it is instead typed with a modified expected type which results by replacing every occurrence of a type parameter by undefined. If this second step fails also, a compile-time error results.

In the second case, the type of the selector is Seq[Int], so as far as the compiler is concerned, it could turn out to be a List[Int] at runtime, in which case it would succeed. It could give a warning that the match could fail (as in fact it does), but the Scala designers decided otherwise: such a warning is given only when the selector's type is sealed.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487