I have a pair of classes that look something like this. There’s a Generator that generates a value based on some class-level values, and a GeneratorFactory that constructs a Generator.
case class Generator[T, S](a: T, b: T, c: T) {
def generate(implicit bf: CanBuildFrom[S, T, S]): S =
bf() += (a, b, c) result
}
case class GeneratorFactory[T]() {
def build[S <% Seq[T]](seq: S) = Generator[T, S](seq(0), seq(1), seq(2))
}
You’ll notice that GeneratorFactory.build accepts an argument of type S and Generator.generate produces a value of type S, but there is nothing of type S stored by the Generator.
We can use the classes like this. The factory works on a sequence of Char, and generate produces a String because build is given a String.
val gb = GeneratorFactory[Char]()
val g = gb.build("this string")
val o = g.generate
This is fine and handles the String type implicitly because we are using the GeneratorFactory.
The Problem
Now the problem arises when I want to construct a Generator without going through the factory. I would like to be able to do this:
val g2 = Generator('a', 'b', 'c')
g2.generate // error
But I get an error because g2 has type Generator[Char,Nothing] and Scala “Cannot construct a collection of type Nothing with elements of type Char based on a collection of type Nothing.”
What I want is a way to tell Scala that the “default value” of S is something like Seq[T] instead of Nothing. Borrowing from the syntax for default parameters, we could think of this as being something like:
case class Generator[T, S=Seq[T]]
Insufficient Solutions
Of course it works if we explicitly tell the generator what its generated type should be, but I think a default option would be nicer (my actual scenario is more complex):
val g3 = Generator[Char, String]('a', 'b', 'c')
val o3 = g3.generate // works fine, o3 has type String
I thought about overloading Generator.apply to have a one-generic-type version, but this causes an error since apparently Scala can’t distinguish between the two apply definitions:
object Generator {
def apply[T](a: T, b: T, c: T) = new Generator[T, Seq[T]](a, b, c)
}
val g2 = Generator('a', 'b', 'c') // error: ambiguous reference to overloaded definition
Desired Output
What I would like is a way to simply construct a Generator without specifying the type S and have it default to Seq[T] so that I can do:
val g2 = Generator('a', 'b', 'c')
val o2 = g2.generate
// o2 is of type Seq[Char]
I think that this would be the cleanest interface for the user.
Any ideas how I can make this happen?
Is there a reason you don’t want to use a base trait and then narrow
Sas needed in its subclasses? The following for example fits your requirements:I’ve made
San abstract type to keep it a little more out of the way of the user, but you could just as well make it a type parameter.