understanding Scala behaviour in auxiliary constructors with a function as parameter

208 views Asked by At

I have been learning Scala, it has been so good so far, sadly i have found certain behaviour that i don't fully understand. I hope you guys can give me some clues, the problem emerged when i coded this class:

class Point(iDim:Int,data:Array[Double],f: Array[Double] => Double) {
...
def this(idim: Int, obj :ObjectThatGenerate_ArrayofDouble, f: Array[Double] => Double){
  this(idim,obj.generateArray(idim),f)
}
}

So when i use these constructors in my main code i need to do this

var p:Point = new Point (idim,obj,f _)

or

var p:Point = new Point (idim,dataArray,f _)

but if i delete the auxiliar constructor i only need to build the object like this:

var p:Point = new Point (idim, dataArray, f)

Why in scala when i have an auxiliary constructor i need to pass a partially implemented function "f _ " and when i don't have an auxiliary constructor i can pass the function directly "f "?, or the character "_" have another meaning in this context?

1

There are 1 answers

0
0__ On BEST ANSWER

As @ghik says, there are type inference limitations when you use method (or in this case constructor) overloading. In general, you should try to avoid overloading, it is almost never good practice.

A work around here would be to use a second parameter list, e.g.

trait Generator {
  def generateArray(idim: Int): Array[Double]
}

class Point(iDim: Int, data: Array[Double], f: Array[Double] => Double) {
  def this(idim: Int, gen: Generator)(f: Array[Double] => Double) {
    this(idim, gen.generateArray(idim), f)
  }
}

val obj = new Generator {
  def generateArray(idim: Int) = new Array[Double](idim)
}

def f(arr: Array[Double]) = arr.sum

new Point(33, obj)(f)

Putting a function argument into a second parameter list is common in Scala, as it allows a convenient syntax for function literals. Because new Point(...) { ... } would be interpreted as extending the class with an anonymous body, the better solution—and getting rid of overloading—is to use the companion object:

object Point {
  def apply(idim: Int, gen: Generator)(f: Array[Double] => Double) =
    new Point(idim, gen.generateArray(idim), f)
}
class Point(iDim: Int, data: Array[Double], f: Array[Double] => Double)

Point(33, obj)(f)
Point(33, obj) { arr => arr.sum / arr.size }

(Note: you can also write the apply method with one parameter list. Because it is not overloaded, you don't have the problem with having to state the underscore)