43

Why does Scala have both unapply and unapplySeq? What is the difference between the two? When should I prefer one over the other?

Dan Burton
  • 53,238
  • 27
  • 117
  • 198

3 Answers3

41

Without going into details and simplifying a bit:

For regular parameters apply constructs and unapply de-structures:

object S {
  def apply(a: A):S = ... // makes a S from an A
  def unapply(s: S): Option[A] = ... // retrieve the A from the S
}
val s = S(a)
s match { case S(a) => a } 

For repeated parameters, apply constructs and unapplySeq de-structures:

object M {
  def apply(a: A*): M = ......... // makes a M from an As.
  def unapplySeq(m: M): Option[Seq[A]] = ... // retrieve the As from the M
}
val m = M(a1, a2, a3)
m match { case M(a1, a2, a3) => ... } 
m match { case M(a, as @ _*) => ... } 

Note that in that second case, repeated parameters are treated like a Seq and the similarity between A* and _*.

So if you want to de-structure something that naturally contains various single values, use unapply. If you want to de-structure something that contains a Seq, use unapplySeq.

Santosh
  • 53
  • 1
  • 7
huynhjl
  • 41,520
  • 14
  • 105
  • 158
18

Fixed-arity vs. variable arity. Pattern Matching in Scala (pdf) explains it well, with mirroring examples. I also have mirroring examples in this answer.

Briefly:

object Sorted {
  def unapply(xs: Seq[Int]) =
    if (xs == xs.sortWith(_ < _)) Some(xs) else None
}

object SortedSeq {
  def unapplySeq(xs: Seq[Int]) =
    if (xs == xs.sortWith(_ < _)) Some(xs) else None
}

scala> List(1,2,3,4) match { case Sorted(xs) => xs }
res0: Seq[Int] = List(1, 2, 3, 4)
scala> List(1,2,3,4) match { case SortedSeq(a, b, c, d) => List(a, b, c, d) }
res1: List[Int] = List(1, 2, 3, 4)
scala> List(1) match { case SortedSeq(a) => a }
res2: Int = 1

So, which do you think is exhibited in the following example?

scala> List(1) match { case List(x) => x }
res3: Int = 1
mbbush
  • 182
  • 12
Julian Fondren
  • 5,459
  • 17
  • 29
  • Ah thanks! That makes sense. Coming from a Haskell perspective, I think of matching on `List(a,b,c)` as desugaring into a match on `a :: b :: c :: Nil`, but it appears Scala doesn't do this; instead, it uses `List`'s `unapplySeq` method. – Dan Burton Nov 27 '11 at 02:06
  • The paper referred in this answer can be found here: http://www.michaelrueegg.name/static/papers/PatternMatchingInScala.pdf – Jaime Febres May 27 '13 at 23:09
  • 1
    @dan Scala can match on Lists they way you expect, by having an unapply method on the :: companion object. As expected this unapply just destructures the List (or rather a ::) into head and tail. Note this works only for Lists, not for any Seq. – Jürgen Strobel Aug 05 '14 at 09:22
  • 1
    Above URLs of paper seem broken. Seems it can now be found at [http://wiki.ifs.hsr.ch/SemProgAnTr/files/PatternMatchingInScala.pdf](http://wiki.ifs.hsr.ch/SemProgAnTr/files/PatternMatchingInScala.pdf) – tossbyte Dec 08 '15 at 21:40
0

Some examples:

scala> val fruit = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)

scala> val List(a, b, c) = fruit
a: String = apples
b: String = oranges
c: String = pears

scala> val List(a, b, _*) = fruit
a: String = apples
b: String = oranges

scala> val List(a, _*) = fruit
a: String = apples

scala> val List(a,rest @ _*) = fruit
a: String = apples
rest: Seq[String] = List(oranges, pears)

scala> val a::b::c::Nil = fruit
a: String = apples
b: String = oranges
c: String = pears

scala> val a::b::rest = fruit
a: String = apples
b: String = oranges
rest: List[String] = List(pears)

scala> val a::rest = fruit
a: String = apples
rest: List[String] = List(oranges, pears)

timothyzhang
  • 730
  • 9
  • 12