I am trying to add numeric operations to a value class that I have defined called Quantity
. Code that I am using this is as follows...
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
}
I use this code as follows...
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)
}
...when I try and compile this code I get the following error..
Error:(29, 63) type mismatch;
found : org.economicsl.auctions.Quantity
required: String
new SortedAskOrders(orders + order, numberUnits + order.quantity)
The following implementation of the +
method which explicitly uses implicit conversions (which I thought should already be in scope!) works.
def + (order: LimitAskOrder[T]): SortedAskOrders[T] = {
new SortedAskOrders(orders + order, Quantity.mkNumericOps(numberUnits) + order.quantity)
}
The compiler does not seem to be able to find the implicit conversion for the numeric +
operator. Thoughts?
I thought that it would be pretty standard to use implicit conversions and the Numeric
trait to create numeric operations for a value class. What am I doing wrong?
The issue is that while you've provided a conversion that supports the enriched operations, it has a lower priority than scala.Predef.any2stringadd
. You can confirm this by shadowing the any2stringadd
name with an implementation that's not applicable here:
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 will always take precedence over implicits defined in companion objects, and Predef
is implicitly imported in all your source files (unless you've enabled -Yno-predef
, which I'd highly recommend, at least for library code).
Unless you're willing to turn off Predef
, the only way around this is to import the conversion (and even if you can turn off Predef
, your users may not be able or willing to).
As a side note, you can make this code a lot more idiomatic by using Numeric
as a type 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
}
}
I.e., instead of having an object instantiating Numeric
and using its ops instance explicitly, you simply provide an implicit instance of the Numeric
type class in your companion object. Now you need an import for any use of the ops syntax methods:
scala> import Numeric.Implicits._
import Numeric.Implicits._
scala> def add(a: Quantity, b: Quantity): Quantity = a + b
add: (a: Quantity, b: Quantity)Quantity
But this is a standard import that other Scala users are more likely to know about, rather than a custom thing that you have to explain separately.