soupybionics soupybionics - 2 months ago 13
Scala Question

List decomposition using case classes and extractor objects

I have created a list as below

List (1, 2, 3, 4)
res4: List[Int] = List(1, 2, 3, 4)


With this, if I do pattern matching as below

res4 match {
case ::(head, tail) => println (head); println(tail);
}


gives me:

1
List(2, 3, 4)


which is expected. Next, if I try the following as an extractor object

(::)(2, List(3, 4))


gives me:

res6: scala.collection.immutable.::[Int] = List(2, 3, 4)


No problems so far. Next, If I try the following:

res4 match {
case :+(head, tail) => println (head); println(tail);
}


I get

List(1, 2, 3)
4


Still no problems. Now, If try the :+ extractor object, the following way:

(:+)(List(1, 2), 3)


I get the following error:

<console>:12: error: scala.collection.:+.type does not take parameters
(:+)(List(1, 2), 3)


What could be the issue here? Is there no :+ extractor object defined ? If no, then how come it works inside the pattern matching?

Answer

What could be the issue here? Is there no :+ extractor object defined ? If no, then how come it works inside the pattern matching?

The problem is that the :+ object supports an unapply method, which is what is used for the Extractor Pattern to work in pattern matching, but doesn't support an apply method:

/** An extractor used to init/last deconstruct sequences. */
object :+ {
  /** Splits a sequence into init :+ tail.
   * @return Some((init, tail)) if sequence is non-empty. None otherwise.
   */
  def unapply[T,Coll <: SeqLike[T, Coll]](
      t: Coll with SeqLike[T, Coll]): Option[(Coll, T)] =
    if(t.isEmpty) None
    else Some(t.init -> t.last)
}

Hence, you get a compile time error. When you think about it, this makes sense, what should be the result of a :+ apply?