4

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, AnyVals 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?

Alexander Temerev
  • 2,654
  • 3
  • 27
  • 34
  • This is a little vague. What do you mean by "play well"? The answer is probably many implicit conversions to and from your new type between other `AnyVal`s. An instance of a `Numeric[YourType]`, would also be useful. – Michael Zajac Mar 10 '15 at 13:15
  • 1
    It's not what you're asking, but I'd suggest looking at Spire, which has efficient implementations of many useful numeric types. And if you want to write you're own it's typeclass-based, so more amenable to use with `AnyVal` than an inheritance-based approach to numerics. – lmm Mar 10 '15 at 13:59

1 Answers1

3

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.

pyrospade
  • 7,870
  • 4
  • 36
  • 52