billpcs
billpcs

Reputation: 633

Function defined with generic types

I searched for a while and I was not able to find a clear and satisfying answer to this question.

Suppose I have this little function to compute the mean value of an Array:

def meanofArray(s:Array[Double]) : Double = s.sum/s.length

the problem is that is Array has to be Double. If for example I have this Array:

val x = Array( 1 , 2 , 3 , 4 ) 

and make the call :

println(meanofArray(x))

I get the error found : Array[Int] required: Array[Double]

How can I implement meanofArray so as to be able to accept any type of Int , Long , Float and Double with no errors ?

My thought was Generic types :

def meanofArray(s:Array[T]) : Double = s.sum/s.length

but I could not get it working.

Any other idea is welcomed !

Upvotes: 2

Views: 86

Answers (3)

drstevens
drstevens

Reputation: 2913

The issue you are running into demonstrates the beauty of type abstraction.

Based solely on the signature of the function you provided

def meanofArray(s:Array[T]) : T

we know quite a bit about what this function can and can not do, as long as it does not do shady things like reflection of course. It can not do things like sort the Array, find the sum, etc. Based on some unfortunate characteristics of Scala, there are some things which we can not be sure it does not do, like use equals or toString for some T.

You need to declare some additional information about the type T in the function signature.

def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double

Here we are declaring that T must have a definition of Numeric[T] defined and in scope at call sites. Numeric[T] provides the definitions of functions required in order to do the calculations you need. We are now declaring that this function may do more things like sort the array or find the sum.

And the implementation you are looking for...

def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double = 
  s.view.map(ev.toDouble).sum / s.length

EDIT: Map s to view of Array[Double] to decrease likelihood of overflow. Taken from @Eastsun's answer. Taking Numeric[T] implicitly is strongly preferred over implicit view from T => Double in my opinion though.

Upvotes: 3

tiran
tiran

Reputation: 2431

This is the cleanest solution i could come up for your question.

def mean[A <% Double : Numeric](seq:Seq[A]) = seq.sum/seq.length

Here, Seq.sum needs an evidence of A conforms to be a Numeric type. (: in the type parameter) And that division to work, the result of seq.sum should be viewable as a Double. Altogether this results a result mean of Double type

Upvotes: 0

Eastsun
Eastsun

Reputation: 18869

Here is what you want(The parameter's type of mean is Seq[A] so that you can use it as mean(List(1, 2, 3)) or mean("ABCDEFG")):

scala> def mean[A](arr: Seq[A])(implicit view: A => Double): Double = 
         (arr map view sum) / arr.size
mean: [A](arr: Seq[A])(implicit view: A => Double)Double

scala> mean(Array(1, 2, 3, 4))
res0: Double = 2.5

Compare with @drstevens 's solution:

scala> def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double = 
     |   ev.toDouble(s.sum) / s.length
meanofArray: [T](s: Array[T])(implicit ev: scala.math.Numeric[T])Double

scala> meanofArray(Array[Byte](111, 111, 111, 111))
res3: Double = -17.0  //Wrong!!!

scala> mean((Array[Byte](111, 111, 111, 111)))
res4: Double = 111.0 //Right !!!

Upvotes: 1

Related Questions