Scala隐式转换函数名称冲突

Tom*_*Tom 6 scala implicit-conversion

我正在使用Scala中一个简单的复数案例类,并希望创建一个在复数,双精度和整数之间工作的加法函数.以下是工作解决方案的简单示例:

case class Complex(re: Double, im: Double) 

implicit def toComplex[A](n: A)(implicit f: A => Double): Complex = Complex(n, 0)

implicit class NumberWithAdd[A](n: A)(implicit f: A => Complex) {
    def add(m: Complex) = Complex(n.re + m.re, n.im + m.im)
}
Run Code Online (Sandbox Code Playgroud)

注意我故意不在复杂的case类中包含add函数.使用上面我可以做到这一切:

scala> val z = Complex(1, 2); val w = Complex(2, 3)
z: Complex = Complex(1.0,2.0)
w: Complex = Complex(2.0,3.0)

scala> z add w
res5: Complex = Complex(3.0,5.0)

scala> z add 1
res6: Complex = Complex(2.0,2.0)

scala> 1 add z
res7: Complex = Complex(2.0,2.0)
Run Code Online (Sandbox Code Playgroud)

我想使用'+'而不是'add,但是这不起作用.我收到以下错误:

Error:(14, 4) value + is not a member of A$A288.this.Complex
              z + 1
               ^
Run Code Online (Sandbox Code Playgroud)

双方z + w1 + z不过仍然可以工作.

我想知道的是为什么将函数名称从'add'改为'+'会打破这个?是否有另一种获得此功能的途径(不需要简单地将add函数放在复杂的case类中)?任何帮助,将不胜感激.

编辑 - 动机

我正在玩幺半群和其他代数结构.我希望能够概括'... WithAdd'函数自动适用于任何具有相应monoid的类:

trait Monoid[A] {
    val identity: A

    def op(x: A, y: A): A
}

implicit class withOp[A](n: A)(implicit val monoid: Monoid[A]) {
    def +(m: A): A = monoid.op(n, m)
}

case class Complex(re: Double, im: Double) {
    override def toString: String = re + " + " + im + "i"
}

class ComplexMonoid extends Monoid[Complex] {
    val identity = Complex(0, 0)

    def op(z: Complex, w: Complex): Complex = {
        Complex(z.re + w.re, z.im + w.im)
    }
}

implicit val complexMonoid = new ComplexMonoid
Run Code Online (Sandbox Code Playgroud)

使用上面我现在可以Complex(1, 2) + Complex(3, 1)给予Complex = 4.0 + 3.0i.这对于代码重用非常有用,因为我现在可以向Monoid和withAdd函数添加额外的函数(例如,将元素应用于元素,给出乘法的幂函数),它适用于任何具有相应的monoid的case类.它只是复杂的数字并试图合并双打,整数等,然后我遇到上面的问题.

Arj*_*jan 0

我会使用常规的class,而不是case class. 然后很容易创建方法来添加或减去这些复数,例如:

class Complex(val real : Double, val imag : Double) {

  def +(that: Complex) =
            new Complex(this.real + that.real, this.imag + that.imag)

  def -(that: Complex) =
            new Complex(this.real - that.real, this.imag - that.imag)

  override def toString = real + " + " + imag + "i"

}
Run Code Online (Sandbox Code Playgroud)

正如源页面所示,它现在将支持看起来像运算符重载的东西(它不是,因为+-是函数而不是运算符)。

implicit class NumberWithAdd和的方法的问题+在于,相同的方法也存在于数字类中,例如IntDouble+的方法基本上NumberWithAdd允许您从一个可以转换的数字开始Complex,并将一个Complex对象添加到第一个项目。也就是说,左手值可以是任何值(只要可以转换),右手值必须是Complex

这非常适合w + z(无需转换w) 和(可以使用to 的1 + z隐式转换)。它失败是因为在类中不可用。由于实际上是,Scala 将寻找可转换为的类中的其他可能匹配项。它还检查,它确实有一个函数,但需要 a作为右手值。(它将匹配需要右手值的函数。)还有其他名为接受的函数,但没有从这些函数想要的左手值进行转换。IntComplexz + 1+Complexz + 1z.+(1)+(i: Int)ComplexNumberWithAdd+ComplexInt+IntComplex

当它在 (case) class 中时,相同的定义确实+有效Complex。在这种情况下, 和w + zz + 1简单地使用该定义。现在案件1 + z有点复杂了。由于Int没有+接受Complex值的函数,Scala 将找到接受值的函数 (in Complex) 并确定是否可以转换IntComplex. 使用隐式函数可以实现这一点,进行转换并执行函数。

+当类中的函数NumberWithAdd被重命名时add,不会与类中的函数混淆,Int因为类中Int没有函数+。因此 Scala 会更加努力地应用该函数add,并且它将执行InttoComplex转换。当您尝试时它甚至会进行转换1 add 2

注意:我的解释可能无法完全描述实际的内部工作原理。