Alexander Temerev Alexander Temerev - 2 months ago 11
Scala Question

Using Scala value classes to define new numeric type

When I first heard of value classes, I thought -- finally! Now I can define my own numeric types sans object allocations! But it turned out harder than I thought.

I want to define my own Decimal type, either

Dec64
(http://dec64.com/) or long-backed decimal for fast monetary calculations. However,
AnyVal
s can't extend
Numeric
, as
Numeric
is not a universal trait. I tried to follow up the Scala code for
Double
, but it is quite complicated, with
AnyValCompanion
,
FractionalProxy
and lots of
private[scala]
marked code, with very helpful comments like Should not be extended in user code.

So, how do I properly define my own numeric value type that can play well together with other Scala numbers?

Answer

Numeric is a type class, so your class wouldn't extend it. Instead, you'd create an instance of the type class for your type. In the example below I use Int for simplicity.

final class MyNum(val i: Int) extends AnyVal
object MyNum {
  implicit val numeric: Numeric[MyNum] = new Numeric[MyNum] {
    override def plus(x: MyNum, y: MyNum): MyNum = new MyNum(x.i + y.i)
    override def minus(x: MyNum, y: MyNum): MyNum = new MyNum(x.i - y.i)
    override def times(x: MyNum, y: MyNum): MyNum = new MyNum(x.i * y.i)
    override def negate(x: MyNum): MyNum = new MyNum(-x.i)
    override def fromInt(x: Int): MyNum = new MyNum(x)
    override def toInt(x: MyNum): Int = x.i
    override def toLong(x: MyNum): Long = x.i.toLong
    override def toFloat(x: MyNum): Float = x.i.toFloat
    override def toDouble(x: MyNum): Double = x.i.toDouble
    override def compare(x: MyNum, y: MyNum): Int = x.i.compare(y.i)
  }
}

If you aren't familiar with type classes, I would recommend Learn You A Haskell For Great Good, particularly its section on type classes. It's just an excellent book anyway, I highly recommend reading the entire thing to fully understand where these ideas come from.