Search code examples
scalatypesparameterization

type parameterization of Scala


Take a concrete method for instance,

def df(f: Float => Float, dt: Float) = (t: Float) => f(t + dt) - f(t)

It can be compiled and works. However, when I tried to define it in a generic way,

def df[T](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)

The compiler said,

"error: type mismatch; found : T;required: String def df[T](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)".

It seemed like the type T couldn't be added. Then I tried another way,

def df[T <: Double](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)

Again It failed,

scala> def df[T <: Double](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)
<console>:7: error: type mismatch;
 found   : Double
 required: T
       def df[T <: Double](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)
                                                             ^

Now All my tricks have been exhausted.

How can I make it?


Solution

  • Regarding your first definition:

    def df[T](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)
    

    This cannot be because we cannot know if there is a "+" or a "-" method for type T.

    Your first definition,

    def df[T <: Double](f: T => T, dt: T) = (t: T) => f(t + dt) - f(t)
    

    Short story - cannot extend from Double and even if you could the + method expects another double, not a T.

    To do this generically we need a unifying trait that applies to all numeric types and which declares all the numeric operators. Unfortunately, this isn't the case in Scala. But we have the next best thing: type classes (see this question for some info: What are type classes in Scala useful for?). Type classes are implemented in Scala using implicits.

    The solution for you would be:

    def df[T](f: T => T, dt: T)(implicit num: Numeric[T]) = (t: T) => num.minus(f(num.plus(t, dt)), f(t))
    

    The method is generic, but it also requires the presence of an object num which knows how to do things like plus, minus and so forth on objects of type T and which is passed implicitly. Fortunately, the Scala library provides instances of Numeric for all primitive numeric types Int, Double etc so you don't have to.

    LATER EDIT:

    As Jesper Nordenberg and Régis Jean-Gilles point out you can actually get your initial expression using an import:

    def df[T](f: T => T, dt: T)(implicit num: Numeric[T]): (T => T) = {
      import num._
      (t: T) => f(t + dt) - f(t)
    }
    

    This is achieved using an implicit conversion also. You can check the source file for Numeric for more information about what's going on: Numeric.scala

    You should be careful though. This solution may have performance problems if you're doing heavy mathematical computation, mostly because of boxing.

    Hope it helps !