tags:

views:

928

answers:

1

I am trying to write some Scala code that needs to do something like:

class Test[Type] { 
   def main {
       SomeFunc classOf[Type]
       val testVal: Type = new Type()
    }
 }

and it's failing. I'm obviously not understanding something about Scala generic parameters. Clearly, the misunderstanding is that in C++, templates essentially function like string substitutions, so new Type() will work as long as the class being passed in has a default constructor. However, in Scala, types are different kinds of objects.

+15  A: 

As you point out, C++ has templates. In short, C++ says "there is a Test for all types T such that Test compiles." That makes it easy to implicitly add constraints on T, but on the down side they're implicit and may be hard for a user of your class to understand without reading code.

Scala's parametric polymorphism (aka generics) work much more like ML, Haskell, Java, and C#. In Scala, when you write "class Test[T]" you are saying "for all T there exists a type Test[T]" without constraint. That's simpler to reason about formally, but it does mean that you have to be explicit about constraints. For instance, in Scala you can say "class Test[T <: Foo]" to say that T must be a subtype of Foo.

C# has a way to add a constraint to T regarding constructors, but unfortunately Scala does not.

There are a couple of ways to solve your problem in Scala. One is typesafe but a bt more verbose. The other is not typesafe and uses an experimental language feature.

The typesafe way looks like

class Test[T](implicit val factory : () => T) {
  val testVal = factory
}

Then you can have a body of factories for types useful in your system

object Factories {
  implicit def listfact[X]() = List[X]()
  implicit def setfact[X]() = Set[X]()
  // etc
}

import Factories._
val t = new Test[Set[String]]

If users of your library need their own factories then they can add their own equivalent of the Factories object. One advantage to this solution is that anything with a factory can be used, whether or not there's a no-arg constructor.

The not-so-typesafe way uses reflection and an experimental feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure

 class Test[T](implicit m : Manifest[T]) {
   val testVal = m.erasure.newInstance().asInstanceOf[T]
 }

With this version you still write

class Foo
val t = new Test[Foo]

However, if there's no no-arg constructor available you get a runtime exception instead of a static type error

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)
James Iry