Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve ambiguous implicit conversion method that takes same input type in Scala?

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?

like image 506
null Avatar asked Jan 10 '15 11:01

null


2 Answers

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 
 }
like image 176
dk14 Avatar answered Dec 14 '22 09:12

dk14


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}
like image 37
lmm Avatar answered Dec 14 '22 10:12

lmm