1
votes

How does one declare in Scala the type signatures needed to implement multiple arithmetic systems that all share a same trait declaring the operations? I thought I had it solved until I tried to add some helper implementations to the base trait/class. In the following code-snippet (which compiles) notice that sq() is defined as this*self not the more obvious this*this. And self() can not be implemented in the trait either (we have to wait until we get to a concrete extending class to implement it).

trait NumberBase [NUMBERTYPE <: NumberBase[NUMBERTYPE]] {
   // type NUMBERTYPE >: this.type
   def *(that: NUMBERTYPE):NUMBERTYPE
   def self:NUMBERTYPE
   def sq:NUMBERTYPE = { this*self }
}

class D(val v:Double) extends NumberBase[D] {
   def self:D = { this }
   def *(that: D):D = { new D(this.v*that.v) }
}

The question/goal is: remove the use of self() or (at least move self()'s implementation into NumberBase) without changing the type signature of * in D. A lot of the fixes in the above link make the derived class unimplementable (things like new D() not being a type returnable by * or * having an unguessable type signature in D). I don't mind some of the signatures getting uglier- but I would like the code to express what this code expresses: that the derived classes work on their own types only and return instances of their own type (they are not moving up and down in a hierarchy).

I found some discussion here that lists some problems (but don't see a solution): http://www.scala-lang.org/node/839 . Some issues you don't run into until you see both the base class and the implementation.

Complete code with the work-around given above (forcing implementing classes to implement self()) is given here: https://github.com/WinVector/AutoDiff where we use the fact different arithmetic systems share the same base-class or trait to write functions that are generic over different arithmetic implementations. This lets us use a wrapping of standard machine arithmetic (like D) or something else (like a number system that computes gradients as a side-effect).

1

1 Answers

3
votes

I think you want to use a self-type, which will ensure that the NumberBase[N] instance will also be an instance of type N:

trait NumberBase[N <: NumberBase[N]] { this: N =>
  def *(that: N): N
  def sq: N = this * this
}

class D(val v: Double) extends NumberBase[D] {
  def *(that: D): D = new D(this.v * that.v)
}

val a = new D(0.5)
val b = new D(0.25)

a * b  // D(0.125)
a.sq   // D(0.25)

BUT, if you really want to define a new type of number, you should do what the Scala libraries themselves do and use the Numeric typeclass.