【问题标题】:Scala implicit conversions and mkNumericOps with value classesScala 隐式转换和具有值类的 mkNumericOps
【发布时间】:2017-08-04 08:41:14
【问题描述】:

我正在尝试将数值运算添加到我定义的名为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 trait 来为值类创建数值运算是非常标准的。我做错了什么?

【问题讨论】:

  • 看起来您需要在文件顶部添加import Quantity._
  • @YuriyGatilin 但是即使没有 import 语句,编译器也能够找到减法运算符的隐式转换。这让我觉得发生了其他事情......
  • 这不是隐含在/超出范围的问题。其他操作(-* 等)似乎可以毫无怨言地编译。不,很明显编译器并没有寻找隐式,因为+ 是字符串 concat op,并且由于所有内容都有字符串表示,因此它不需要隐式。我知道至少有一种解决方法,但我希望对此有更深入了解的人会以一种干净简单的方法来关闭字符串连接。

标签: scala implicit-conversion implicit value-class


【解决方案1】:

问题在于,虽然您提供了支持丰富操作的转换,但它的优先级低于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 类型类的隐式实例即可。现在你需要一个 import 以便 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 用户更可能知道的标准导入,而不是您必须单独解释的自定义内容。

【讨论】:

  • 我正在编写库代码,经过一番挖掘,我认为使用 Yno-predef 编译器标志是可行的方法。这将如何影响可能将我的 jar 文件添加到各自项目的下游 Java/Scala 用户?我注意到猫不使用这个编译器标志...
  • @davidrpugh 它在运行时或对已发布的工件没有任何影响(尽管名称暗示它可能)——它所做的只是跳过插入隐含的import Predef._,你通常在编译器正在处理您的源文件。
猜你喜欢
  • 1970-01-01
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
相关资源
最近更新 更多