I would not compromise the original equals , hashCode and therefore == for the case class. IMHO the most idiomatic solution here from the point of view of functional programming is to use a class like:
case class Person(firstName: String, lastName: String) trait Equal[A] { def eq(a1: A, a2: A): Boolean } object Equal { def areEqual[A : Equal](a1: A, a2: A): Boolean = implicitly[Equal[A]].eq(a1, a2) implicit object PersonEqual extends Equal[Person] { override def eq(a1: Person, a2: Person): Boolean = a1.firstName.equalsIgnoreCase(a2.firstName) && a1.lastName.equalsIgnoreCase(a2.lastName) } }
In a REPL session:
scala> import Equal.areEqual import Equal.areEqual scala> val p1 = Person("John", "Doe") p1: Person = Person(John,Doe) scala> val p2 = p1.copy(firstName = "john") p2: Person = Person(john,Doe) scala> areEqual(p1, p2) res0: Boolean = true scala> val p3 = p1.copy(lastName = "Brown") p3: Person = Person(John,Brown) scala> areEqual(p1, p3) res1: Boolean = false
Thus, if you need to provide a different equality value for Person in this context, you can simply implement your version of Equal[Person] without touching anything. For example: at a given point in your code, two instances of Person are equal if they have the same name:
implicit object PersonLastnameEqual extends Equal[Person] { override def eq(a1: Person, a2: Person): Boolean = a1.lastName.equalsIgnoreCase(a2.lastName) }
REPL session:
scala> val p1 = Person("John", "Doe") p1: Person = Person(John,Doe) scala> val p2 = p1.copy(firstName = "Mary") p2: Person = Person(Mary,Doe) scala> areEqual(p1, p2) res0: Boolean = true
source share