Learn About Scala's Case Classes and Built-in Extractor Functions

Case classes in Scala are very handy because they have build extractor capability and other free features.

We can define a case class and then use it conveniently in the code:

import scala.collection.mutable

case class MyFeature(a: Int, b: Int, c: Int, d: Int)

object Solution extends App {
  val feature = MyFeature(1,2,3,4)
  val queue = mutable.Queue.empty[MyFeature]
  while(queue.nonEmpty) {
    queue.dequeue() match {
      case MyFeature(_,b,_,_) => // do something with B

The problem starts when we have to deal with model evolution. What if we need to add a new parameter to MyFeature and update the case class definition to MyFeature(a: Int, b: Int, c: Int, d: Int, e: Int)

If we launch our code as is, Scala will start to complain:

Wrong number of arguments for the extractor, found: 4, expected: 5

It means that you will have to find all places in the codebase where the built-in MyFeature extractor has been used and fix all of them.

case MyFeature(_,b,_,_,e) => ...
If you expect a lot of extensions of your Scala case class, it is better to make a custom extractor instead of using a build-in extractor

First, let's implement a custom extractor

object BParameter {
  def unapply(f: MyFeature): Option[Int] = {

Refactor the original loop with our new extractor

while(queue.nonEmpty) {
  queue.dequeue() match {
    case BParameter(b) => // do something with B

This code will not depend on MyFeature extensions. Additionally, you can define many unapply methods with different parameters to extract the same type of objects from different classes.

In conclusion, while Scala's case classes provide built-in extractors for convenience, they can become problematic when dealing with model evolution. To avoid issues, consider implementing custom extractors, which offer more flexibility and adaptability as your case class evolves.

Did you find this article valuable?

Support Maksim Martianov by becoming a sponsor. Any amount is appreciated!