2
votes

I have a problem about assignment via pattern matching in scala. Let's say I have the following:

a. Seq.unapplySeq(List(1))

b. val Seq(z) = List(1)

c. val c = List(1) match {
  case Seq(e) => e
}

d. List.unapplySeq(Seq(1))

e. val List(a) = Seq(1)

f. val b = Seq(1) match {
  case List(e) => e
}

Only (d) doesn't compile and others compile and run right.

I know that unapplySeq of List is defined in SeqFactory as:

abstract class SeqFactory[CC[X] <: Seq[X] with GenericTraversableTemplate[X, CC]] extends GenSeqFactory[CC] with TraversableFactory[CC] {
    applySeq[A](x: CC[A]): Some[CC[A]] = Some(x)
}

Because CC is List, Seq in (d) won't type check.

Seems like (a), (b) and (c) are in one group and (d), (e) and (f) are in the other.

In my understanding, destruction of (f) will actually call (d) because what the pattern matching in (f) does is use List to destruct Seq(1).

My question is why (e) and (f) are still right in the case (d) does not compile.

2

2 Answers

0
votes

(e) is translated to (f), correct. But if you look at https://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#pattern-sequences, the only requirement for unapplySeq is on result type, not argument type. So my guess (the specification doesn't actually specify this and I can't check at the moment) is that (f) tests that it's argument is a List before calling unapplySeq, i.e. it's internally something like

Seq(1) match {
    case canUnapplySeq: List[Int] => 
        ... List.unapplySeq(canUnapplySeq) // (d) only invoked here
}

Note that the argument applies to unapply as well.

0
votes

I may have a answer for you which doesn't talk about SeqFactory:

  1. (e) compiles and runs because Seq(1) is an shortcut for Seq.apply(2) which conveniently always give a List instance, ie. here List(2)*. Thus it's assimilable with (b) case.
  2. (f) compiles and runs for the same foresaid reason (Seq(1) gives a List(1) which match case List(e))
  3. (d) doesn't compile because what I've said about (e) case in first point is about instance and not type, ie. Seq(1) is an instance of List but of type Seq. Here's a demonstration:

    List.unapplySeq(Seq(1))
    

    is equivalent to

    List.unapplySeq(List(1).asInstanceOf[Seq[Int]])
    

    and give:

    *error: type mismatch;*
    

    whereas:

    List.unapplySeq(List(1))
    

    is equivalent to:

    List.unapplySeq(Seq(1).asInstanceOf[List[Int]])
    

    and both give:

    Some(List(1))
    

But, here's the difficulty, you have a point about (f) pattern matching curiosity: It behaves as if scala internally type it as a List and not as a Seq.

Here's my explanation pattern matching doesn't care about input's type. It's simple as that (cf. https://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#variable-patterns for example). Here's a demonstration:

    var foo: Seq[Int] = List(1)
    foo match { case _: List[Int] => true } // gives true
    foo = 1 to 3
    foo match { case _: List[Int] => true } // throws scala.MatchError

Hope it helps.

*To be clearer, if, in another scala implementation, Vector(2) would have been given instead by this apply, error would have popped up not during compiling but while running (Alexey Romanov made a good point about this).