【问题标题】:Scala Implicit Conversion Function Name ClashesScala 隐式转换函数名称冲突
【发布时间】:2016-07-25 23:11:59
【问题描述】:

我正在使用 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)
}

注意,我故意不在复杂案例类中包含 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)

我想使用“+”而不是“添加”,但这不起作用。我收到以下错误:

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

z + w1 + z 仍然有效。

我想知道的是为什么将函数名从“add”更改为“+”会破坏这个?是否有获得此功能的替代途径(而不是将 add 函数简单地放在复杂案例类中)?任何帮助将不胜感激。

编辑 - 动机

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

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

使用上面的我现在可以做Complex(1, 2) + Complex(3, 1)Complex = 4.0 + 3.0i。这对于代码重用非常有用,因为我现在可以向 Monoid 和 withAdd 函数添加额外的函数(例如将 op n 次应用于元素,为乘法提供幂函数),并且它适用于任何具有相应 monoid 的案例类.只有使用复数并尝试合并双精度数、整数等,我才会遇到上述问题。

【问题讨论】:

  • 无法通过隐式转换添加已存在的方法stackoverflow.com/a/4444122/1594980
  • 您链接到的问题是试图隐式覆盖具有相同签名的函数。类型 Int、Double 或我的 Complex 类都没有一个方法 + 带有一个 Complex 类型的参数。

标签: scala implicit-conversion


【解决方案1】:

我会使用普通的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"

}

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

implicit class NumberWithAdd 及其方法+ 的问题是同样的方法也存在于数字类中,例如IntDoubleNumberWithAdd+ 方法基本上允许您从一个可以转换为 Complex 的数字开始,然后将 Complex 对象添加到第一项。即左边的值可以是任何东西(只要可以转换),右边的值必须是Complex

这对w + z(无需转换w)和1 + zIntComplex 的隐式转换可用)非常有效。 z + 1 失败,因为 +Complex 类中不可用。 由于z + 1 实际上是z.+(1),Scala 将在Complex 可以转换成的类中寻找+(i: Int) 的其他可能匹配项。它还检查NumberWithAdd,它确实具有+ 函数,但需要Complex 作为右手值。 (它会匹配需要 Int 作为右手值的函数。)还有其他名为 + 的函数确实接受 Int,但没有从 Complex 转换为这些函数想要作为左手值的函数.

+ 的相同定义在(案例)类Complex 中确实有效。在这种情况下,w + zz + 1 都只使用该定义。 1 + z 的案例现在有点复杂。由于Int 没有函数+ 接受Complex 值,因此Scala 将找到接受Complex 的函数并确定是否可以将Int 转换为Complex .这可以使用隐式函数,进行转换并执行函数。

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

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

【讨论】:

  • 我知道这是一个解决方案,但我在我的问题中提到我不想将这些函数包含在案例类中。我的部分问题是是否有其他方法可以做到这一点。
  • @Tom 我已经扩展了我的答案。这应该希望能更清楚地说明问题。
  • 因此,如果我们不/不能修改class Complex,那么我们需要做的就是让z + 1工作,将def +(i: Int): Complex = this.+(toComplex(i))添加到NumberWithAdd班级。但这只会涵盖z + 1 (Int)。我们必须添加相同的内容来覆盖z + 1L(长)和z + 1.1(双)等。*sigh*
  • @Arjan 感谢您花时间充实您的答案。似乎获得不同的解决方案比我想象的要难。
  • @jwvh 我想IntDouble 这样的类有几个函数+ 都接受不同的参数(并且有不同的返回类型)是有原因的。
猜你喜欢
  • 2012-02-21
  • 1970-01-01
  • 2011-04-15
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-10
相关资源
最近更新 更多