maackle
maackle

Reputation: 2134

Why aren't type parameters allowed in Scala auxiliary constructors?

Say I'm defining a simple 2D point class in Scala, and I want to be able to construct it with various types:

class Point(x:Float, y:Float) {
    this(x:Double, y:Double) = this(x.toFloat, y.toFloat)
    this(x:Int, y:Int) = this(x.toFloat, y.toFloat)
    // etc...
}

I want to boil this down using a template, such as:

class Point(x:Float, y:Float) {
    this[T](x:T, y:T) = this(x.toFloat, y.toFloat)
}

I know this won't work anyway, since T could be a type for which toFloat isn't defined, but the compiler error I get is:

no type parameters allowed here

Is this just unsupported in Scala? If so, why, and is there any simple way to get around this?

Upvotes: 14

Views: 1664

Answers (2)

user166390
user166390

Reputation:

I played with this for awhile, getting as "close" as...

class Point(x:Float, y:Float) {
  def this[T <: Any { def toFloat: Float }](x:T, y:T) = this(x.toFloat, y.toFloat)
}

...which results in "error: no type parameters allowed here" (just as per the post) and then I realized...

If the initializer could take type parameters it would be ambiguous with the class parameters, if any. Not that this couldn't be worked about in the language specification... but it is a more complex case at the very least. There might also be Java interoperability issues.

Imagine:

class Foo[T](x: T) {
   def this[X](z: X) = ...
}
new Foo[Int](42) // T is Int? X is ...? Or ...?

Personally I wish Scala followed an Eiffel-like pattern (only named constructors or "factory methods"), but alas, that would not be Scala.

Happy coding.

Upvotes: 3

Kipton Barros
Kipton Barros

Reputation: 21112

Scala's class constructors (unlike Java's) can't take type parameters, only the class itself can. As to why Scala made this design choice, I assume the main reason is simplicity.

If you want a secondary "builder" method that is generic, the natural thing to do is define it on the companion object. For example,

object Point {
  def build[T : Numeric](x: T, y: T) = {
    val n = implicitly[Numeric[T]]
    new Point(n.toFloat(x), n.toFloat(y))
  }
}

class Point(val x:Float, val y:Float)

val p = Point.build(1, 2) // Companion object's builder
p.x + p.y

Here I've used the Numeric typeclass to get a generic toFloat method.

Upvotes: 15

Related Questions