为什么隐式转换不适用于第二个参数?

时间:2017-08-08 22:00:10

标签: scala operator-overloading implicit-conversion implicit

当我尝试编译以下代码时

case class A(x: Int = 0)

object Conversions {
  case class Converter[T](c: T) {
    def +[U](that: Converter[U]): String = "worked"
  }
  implicit def convert(c: A): Converter[A] = Converter[A](c)
  implicit def convert(c: Int): Converter[Int] = Converter[Int](c)
}

object Main extends App {
    import Conversions._
    val a: String = A() + A()
    val b: String = 1 + A() // FAIL
    val c: String = A() + 1
    val d: Int = 1 + 1
}

我收到以下错误消息

  

错误:类型不匹配;找一个; required:String

IntA之间有什么区别,因此ba传递时表达式c失败了?我该怎么做才能编译?

3 个答案:

答案 0 :(得分:1)

首先,差异Int已经有另一个+方法,它需要另一个Int并返回Int; 如果您向A添加类似的方法,则会遇到类似的失败:

case class A(x: Int = 0) {
  // this would be similar to Int's plus operation:
  def +(other: A): A = A(x + other.x)
}

// now these would behave "symmetrically": 
val a: A = A() + A()
val b: String = 1 + A() // FAIL
val c: String = A() + 1 // FAIL
val d: Int = 1 + 1

至于为什么这个事实导致了这个特定的失败 - 这更棘手,我认为它与编译器试图选择"选择"的顺序有关。基于左右参数的正确隐式转换。

答案 1 :(得分:0)

如果您将算术+运算符替换为plus

def plus[U](that: Converter[U]): String = "worked"

您的隐式转化应该按预期方式运行:

val a = A() plus A()  // worked
val b = 1 plus A()    // worked
val c = A() plus 1    // worked
val d = 1 plus 1      // worked

而不是IntA之间的直接隐式转换,您的隐式转换介于Int和类Converter之间(以及A和{之间{1}})。当编译器看到ConverterInt定义良好的方法时,显然很难对A+之间的转换规则进行排序 - 除非您提供直接隐式IntInt之间的转换,如下例所示:

A

答案 2 :(得分:0)

我对此尝试成功的原因并不清楚,但我希望它对其他用户有用。在这里,我同时匹配两个参数,以尽量减少Scala强制纠正隐式转换树的尝试。

object Defs {
    case class A(x: Int = 1)
    case class B(x: Int = 2)

    trait IsExpr[T] {
        def conv(v: T): Int
    }

    implicit object aIsExpr extends IsExpr[A] {
        override def conv(v: A): Int = v.x
    }

    implicit object bIsExpr extends IsExpr[B] {
        override def conv(v: B): Int = v.x
    }

    implicit def canSum1[A, B](a: A, b: B)(implicit ca: IsExpr[A], cb: IsExpr[B]): Int = ca.conv(a) + cb.conv(b)
    implicit def canSum2[A](a: A, b: Int)(implicit ca: IsExpr[A]): Int = ca.conv(a) + b
    implicit def canSum3[A](a: Int, b: A)(implicit ca: IsExpr[A]): Int = a + ca.conv(b)

    implicit def convert[A](a: A) = new {
        def +[B](b: B)(implicit f: (A, B) => Int): Int = f(a, b)
    }
}

object Main extends App {
    import Defs._
    val a: Int = A() + A()
    val b: Int = 1 + A()
    val c: Int = A() + 1
    val d: Int = 1 + 1
}