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

时间:2017-03-14 06:02:03

标签: scala implicit-conversion implicit value-class

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

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特征为值类创建数值运算是非常标准的。我做错了什么?

1 个答案:

答案 0 :(得分:4)

问题在于,虽然您提供了支持丰富操作的转化,但其优先级低于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

导入的隐含将始终优先于随播对象中定义的隐含,并且Predef会隐式导入所有源文件中(除非您已启用-Yno-predef,我这样做了强烈推荐,至少对于图书馆代码而言。)

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

作为旁注,您可以使用Numeric作为类型类,使此代码更加惯用:

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类型类的隐式实例。现在您需要导入任何使用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用户更有可能了解的标准导入,而不是您必须单独解释的自定义内容。