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]
queue.enqueue(feature)
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) => ...
First, let's implement a custom extractor
object BParameter {
def unapply(f: MyFeature): Option[Int] = {
Some(f.b)
}
}
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.