Hello!
I’m studying the type classes by going through the excellent manual “Essential Scala”. I’m doing one of the exercises and trying to write a Type Class that “should compare two values of type A and return a Boolean”; as an example I’m using a Person
Class:
package language.typeclass.equivalence
case class Person(name: String, email: String)
so the T.C. is
package language.typeclass.equivalence
trait Equal[A] {
def equal(a1: A, a2: A): Boolean
}
object Equal {
def apply[A](implicit instance: Equal[A]): Equal[A] = instance
}
and the relative instances are:
object NameAndEmailEqual {
implicit val nameAndEmailEqual: Equal[Person] =
new Equal[Person] {
def equal(p1: Person, p2: Person): Boolean =
p1.name == p2.name && p1.email == p2.email
}
}
object EmailEqual {
implicit val emailEqual: Equal[Person] =
new Equal[Person] {
def equal(p1: Person, p2: Person): Boolean =
p1.email == p2.email
}
}
that is, one to compare two Persons by Email, and the other by Name and Email.
Now I enrich the type with the interface as follows:
object Eq {
implicit class EqualEm[A](a1: A) {
import EmailEqual._
def isEqByEmail(a2: A)(implicit equal: Equal[A]): Boolean =
equal.equal(a1, a2)
}
implicit class EqualNaE[A](p1: A) {
import NameAndEmailEqual._
def isEqByNameAndEmail(p2: A)(implicit equal: Equal[A]): Boolean =
equal.equal(p1, p2)
}
}
But, when I try it on the REPL:
scala> import language.typeclass.equivalence.Person
import language.typeclass.equivalence.Person
scala> import language.typeclass.equivalence.Equal
import language.typeclass.equivalence.Equal
scala> import language.typeclass.equivalence.Equal._
import language.typeclass.equivalence.Equal._
scala> import language.typeclass.equivalence.Eq._
import language.typeclass.equivalence.Eq._
scala> val p1 = Person("Guido", "[email protected]")
p1: language.typeclass.equivalence.Person = Person(Guido,[email protected])
scala> val p2 = Person("Anna", "[email protected]")
p2: language.typeclass.equivalence.Person = Person(Anna,[email protected])
this is the result:
scala> p1.isEqByEmail(p2)
^
error: could not find implicit value for parameter equal: language.typeclass.equivalence.Equal[language.typeclass.equivalence.Person]
But I thought that I imported the implicits in scope by importing the relevant Instances in the single interfaces methods. OK. So I do:
scala> import language.typeclass.equivalence.EmailEqual._
import language.typeclass.equivalence.EmailEqual._
scala> p1.isEqByEmail(p2)
res1: Boolean = true
IT WORKS ( or so it seems). But I want use both extension, so I import the other instance:
scala> import language.typeclass.equivalence.NameAndEmailEqual._
import language.typeclass.equivalence.NameAndEmailEqual._
and now:
scala> p1 isEqByEmail p2
^
error: ambiguous implicit values:
both value emailEqual in object EmailEqual of type => language.typeclass.equivalence.Equal[language.typeclass.equivalence.Person]
and value nameAndEmailEqual in object NameAndEmailEqual of type => language.typeclass.equivalence.Equal[language.typeclass.equivalence.Person]
match expected type language.typeclass.equivalence.Equal[language.typeclass.equivalence.Person]
So, the question: how can avoid this ambiguity?
Thanks.