As peers the example below, I am trying to make a case class
that can hold items of type SomeResult[T]
without having to know what T is. This works fine in the case of Rawr
, which can hold a Set
of SomeResult[_]
, however when I add a second field to try and work with on the same principle (i.e. a single element who's content we don't care about, and a set of elements), I get the following error
[error] /Users/matthewdedetrich/temp/src/main/scala/Main.scala:15: type arguments [_$2] do not conform to class SomeResult's type parameter bounds [A <: T]
[error] case class Bleh(oneThing:SomeResult[_],moreThings:Set[SomeResult[_]]) // This doesn't
Here is the sample code
trait T {
}
case class First(int:Int) extends T
case class Second(int:Int) extends T
case class SomeResult[A <: T](name:String, t:A)
case class Rawr(multipleThings:Set[SomeResult[_]]) // This works
case class Bleh(oneThing:SomeResult[_],moreThings:Set[SomeResult[_]]) // This doesn't
There is a suggestion to use a [+A <: T]
as a type bound instead of a wildcard, however the following code doesn't work when doing this
val t = Set(First(3),Second(5))
def someFunc[A <: T](thing:A) = {
thing match {
case First(_) => SomeResult("a",First(10))
case Second(_) => SomeResult("b",Second(15))
case _ => throw new IllegalArgumentException("rawr")
}
}
val z = t.map{
case x:First => someFunc(First(5))
case y:Second => someFunc(Second(5))
case _ => throw new IllegalArgumentException("rawr")
}
val z2 = Rawr(z)
Which then provides the error
[error] found : scala.collection.immutable.Set[SomeResult[Product with Serializable with T]]
[error] required: Set[SomeResult[T]]
[error] Note: SomeResult[Product with Serializable with T] <: SomeResult[T], but trait Set is invariant in type A.
[error] You may wish to investigate a wildcard type such as `_ <: SomeResult[T]`. (SLS 3.2.10)
Which is why I used wildcard types in the first place. Funnily enough, if you try to provide a return type to sumFunc, you get the exact same problem (where the scala compiler error suggests that you should use Wildcard types)
EDIT 2: I have actually managed to get the code to compile by doing this
def someFunc[A <: T](thing:A):SomeResult[A] = {
thing match {
case First(_) => SomeResult("a",First(10)).asInstanceOf[SomeResult[A]]
case Second(_) => SomeResult("b",Second(15)).asInstanceOf[SomeResult[A]]
case _ => throw new IllegalArgumentException("rawr")
}
}
def z[A <: T]:Set[SomeResult[A]] = t.map{
case x:First => someFunc(First(5)).asInstanceOf[SomeResult[A]]
case y:Second => someFunc(Second(5)).asInstanceOf[SomeResult[A]]
case _ => throw new IllegalArgumentException("rawr")
}
Im not sure if its "idiomatic" or "right", but its the only way to get the Serializable with Product
out of the type signature. I have no idea why Scala infers this when the result type is clearly a subtype of T
Why don't just use T
instead of _
?:
case class Bleh(oneThing:SomeResult[T],moreThings:Set[SomeResult[T]]) // Compiles
Then e.g. Bleh(SomeResult("", First(0)), Set())
works fine.
Naming a trait as T
is a bit unfortunate, it's easily confused with type parameter.
Answer to EDIT2:
Slightly edited and working:
val t: Set[T] = Set(First(3), Second(5))
def someFunc[A <: T](thing: A): SomeResult[T] = {
thing match {
case First(_) => SomeResult("a", First(10))
case Second(_) => SomeResult("b", Second(15))
case _ => throw new IllegalArgumentException("rawr")
}
}
def z[A <: T]: Set[SomeResult[T]] = t.map {
case x: First => someFunc(First(5))
case y: Second => someFunc(Second(5))
case _ => throw new IllegalArgumentException("rawr")
}
You were forcing a type A
to Set
, but Set
is invariant (compiler was unable to make a Set[T]
when got t
of type First
/Second
[old method signatures was with A
]).