Scala 隐式转换和具有值 类 的 mkNumericOps

Scala implicit conversions and mkNumericOps with value classes

我正在尝试将数字运算添加到我定义的名为 Quantity 的值 class 中。我正在使用的代码如下...

import scala.language.implicitConversions

case class Quantity(value: Double) extends AnyVal

object Quantity {
  implicit def mkNumericOps(lhs: Quantity): QuantityIsNumeric.Ops = QuantityIsNumeric.mkNumericOps(lhs)
}

object QuantityIsNumeric extends Numeric[Quantity] {

  def plus(x: Quantity, y: Quantity): Quantity = Quantity(x.value + y.value)

  def minus(x: Quantity, y: Quantity): Quantity = Quantity(x.value - y.value)

  def times(x: Quantity, y: Quantity): Quantity = Quantity(x.value * y.value)

  def negate(x: Quantity): Quantity = Quantity(-x.value)

  def fromInt(x: Int): Quantity = Quantity(x.toDouble)

  def toInt(x: Quantity): Int = x.value.toInt

  def toLong(x: Quantity): Long = x.value.toLong

  def toFloat(x: Quantity): Float = x.value.toFloat

  def toDouble(x: Quantity): Double = x.value

  def compare(x: Quantity, y: Quantity): Int = x.value compare y.value
}

我使用这段代码如下...

class SortedAskOrders[T <: Tradable] private(orders: immutable.TreeSet[LimitAskOrder[T]], val numberUnits: Quantity) {

  def + (order: LimitAskOrder[T]): SortedAskOrders[T] = {
    new SortedAskOrders(orders + order, numberUnits + order.quantity)
  }

  def - (order: LimitAskOrder[T]): SortedAskOrders[T] = {
    new SortedAskOrders(orders - order, numberUnits - order.quantity)
  }

  def head: LimitAskOrder[T] = orders.head
  def tail: SortedAskOrders[T] = new SortedAskOrders(orders.tail, numberUnits - head.quantity)
}

...当我尝试编译此代码时出现以下错误...

Error:(29, 63) type mismatch;
 found   : org.economicsl.auctions.Quantity
 required: String
      new SortedAskOrders(orders + order, numberUnits + order.quantity)

以下显式使用隐式转换(我认为应该已经在范围内!)的 + 方法的实现有效。

def + (order: LimitAskOrder[T]): SortedAskOrders[T] = {
  new SortedAskOrders(orders + order, Quantity.mkNumericOps(numberUnits) + order.quantity)
}

编译器似乎无法找到数字 + 运算符的隐式转换。想法?

我认为使用隐式转换和 Numeric 特征为值 class 创建数值运算是非常标准的。我做错了什么?

问题是,虽然您提供了支持丰富操作的转换,但它的优先级低于 scala.Predef.any2stringadd。您可以通过使用此处不适用的实现隐藏 any2stringadd 名称来确认这一点:

scala> implicit def any2stringadd(i: Int): Int = i
any2stringadd: (i: Int)Int

scala> def add(a: Quantity, b: Quantity): Quantity = a + b
add: (a: Quantity, b: Quantity)Quantity

Imported implicits 将始终优先于伴随对象中定义的 implicits,并且 Predef 被隐式导入到所有源文件中(除非您启用了 -Yno-predef,我强烈推荐,至少对于库代码)。

除非您愿意关闭 Predef,否则解决此问题的唯一方法是导入转换(即使您可以关闭 Predef,您的用户也可能无法或愿意)。

作为旁注,您可以通过使用 Numeric 作为类型 class:

使这段代码更加地道
case class Quantity(value: Double) extends AnyVal

object Quantity {
  implicit val quantityNumeric: Numeric[Quantity] = new Numeric[Quantity] {
    def plus(x: Quantity, y: Quantity): Quantity = Quantity(x.value + y.value)
    def minus(x: Quantity, y: Quantity): Quantity = Quantity(x.value - y.value)
    def times(x: Quantity, y: Quantity): Quantity = Quantity(x.value * y.value)
    def negate(x: Quantity): Quantity = Quantity(-x.value)
    def fromInt(x: Int): Quantity = Quantity(x.toDouble)
    def toInt(x: Quantity): Int = x.value.toInt
    def toLong(x: Quantity): Long = x.value.toLong
    def toFloat(x: Quantity): Float = x.value.toFloat
    def toDouble(x: Quantity): Double = x.value
    def compare(x: Quantity, y: Quantity): Int = x.value compare y.value
  }
}

即,不是让对象实例化 Numeric 并显式使用其 ops 实例,您只需在伴随对象中提供 Numeric 类型 class 的隐式实例。现在您需要导入 any 使用 ops 语法方法:

scala> import Numeric.Implicits._
import Numeric.Implicits._

scala> def add(a: Quantity, b: Quantity): Quantity = a + b
add: (a: Quantity, b: Quantity)Quantity

但这是其他 Scala 用户更可能知道的标准导入,而不是您必须单独解释的自定义内容。