Search code examples
scalapattern-matchingdefault-value

Add a parameter to a case class with a default


I have an existing case class, to which I'd like to add an additional constructor parameter, with a default value, and not disturb existing code that does pattern matching, yet I can't figure out the right approach.

Ie. I have:

case class Foo(a: Int, b: Int)

def bar(n: Foo) = n match {
  case Foo(1, 2) => true
  case _ => false
}

Now suppose I need to add an additional parameter, c, to Foo. Ie.

case class Foo(a: Int, b: Int, c: Boolean = true)

In all existing use cases, the parameter c would be true, hence it has that default value. Yet in some new use cases, there is a need to pass false for this.

So it seems sensible to add another parameter, with a default value of true. Yet as soon as I do this, the pattern match in bar becomes a syntax error. This seems wrong, since I added the default = true to ensure that existing constructor calls wouldn't need to be modified.

How can I do this and leave the old pattern matches unchanged?

Update: Note that I also don't want to have to rewrite all the existing instantiations of Foo. @som-snytt pointed out that I could add another parameter as Foo(a: Int, b: Int)(c: Boolean = true), which would be perfect except that it causes existing calls, such as Foo(1,2) to fail (they have to be rewritten as Foo(1,2)()). I'm looking for a way to add a new parameter only for some use-cases, and avoid rewriting by having a default which works for everywhere else.


Solution

  • The case Foo syntax is not calling a constructor, rather a method called unapply on object Foo. case classes autogenerate various boilerplate including the companion object and the unapply.

    unapply only has one parameter, the object being matched. This keeps you from overloading it since you can't overload on return value in Java/Scala.

    So in short you can't do quite what you want.

    You can, however, make an extractor with a different name. Here's someone who just added an underscore: http://x3ro.de/multiple-unapply-methods-for-pattern-matching-in-scala/

    perhaps it would be nicer when possible to use a more meaningful name for the extractor variants though.

    Here's some more info on how it all works: http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

    You can write out everything a case class does "by hand" and do it differently, such as a different unapply, but it would be pretty annoying assuming you care about equals, hashCode, toString, and all that. If you did that you could maybe avoid having to change existing code but it seems unlikely to be worth it.

    Here is an example someone posted: https://gist.github.com/okapies/2814608