I have read other same question here, however the case is too specific for certain built-in class. I want to ask for simple case here and hoping for general answer.
So I have this code:
object ScalaApp {
case class Point(x: Int, y: Int);
case class Rational(n: Int, d: Int) {
def \(i: Int): List[Int] = n :: d:: i :: Nil
}
case class MyObject(x: Int, y: Int, z: Int) {
def \(i: Int): List[Int] = x :: y:: i :: Nil
}
implicit def pointToRational(p: Point): Rational = Rational(p.x + 1, p.y * 2)
implicit def pointToMyObject(p: Point): MyObject = MyObject(p.x, p.y, p.x+p.y)
def main(args: Array[String]) {
val p = Point(5, 7)
val result = p \ 6 // compile error here
}
}
We can see the error happened when p
is applied to \
method then the implicit conversion is triggered. Scala compiler will try to look for any imported or local class that has def \(i: Int)
method defined. If found, then it will try to look for any implicit conversion method that take Point
type and return the type of object that has def \(i: Int)
method signature.
It happened there are two class that has def \
method here: Rational
and MyObject
, and there are two implicit methods too: pointToRational
and pointToMyObject
for those two classes.
However since both Rational
and MyObject
has def \
defined, hence the ambiguous compile error occured because it can't decide which one should be taken.
Error:(30, 22) type mismatch;
found : p.type (with underlying type ScalaApp.Point)
required: ?{def \(x$1: ? >: Int(6)): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method pointToRational in object ScalaApp of type (p: ScalaApp.Point)ScalaApp.Rational
and method pointToObj in object ScalaApp of type (p: ScalaApp.Point)ScalaApp.MyObject
are possible conversion functions from p.type to ?{def \(x$1: ? >: Int(6)): ?}
val result = p \ 6
^
So my question, is there a way to resolve the ambiguous implicit error but still retaining the two def \
methods or without removing one of implicit conversion method?
You can do that:
scala> val result = (p: Rational) \ 6
result: List[Int] = List(6, 14, 6)
scala> val result = (p: MyObject) \ 6
result: List[Int] = List(5, 7, 6)
So you can just put it in your code instead of :
val result = p \ 6 // compile error here
Another way it's moving your implicits into separate object:
object Implicits {
implicit def pointToRational(p: Point): Rational = Rational(p.x + 1, p.y * 2)
implicit def pointToMyObject(p: Point): MyObject = MyObject(p.x, p.y, p.x+p.y)
}
def main(args: Array[String]) {
val p = Point(5, 7)
import Implicits.pointToMyObject
val result = p \ 6
}
Not really. You can sometimes abuse the implicit resolution order (e.g. by moving one of the implicits into a companion object, or a trait that a companion object extends, so that it would only be looked at after looking for implicits in your immediate scope). But generally you should probably just ensure that there's only one suitable implicit in scope. Remember you can always create inner scopes with {}
and import an implicit just in that scope:
val result = {import someImplicit; p \ 6}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With