I'm trying to copy() a Scala case class which has a type param. At the call site, the type of the value is Foo[_].
This compiles as expected:
case class Foo[A](id: String, name: String, v1: Bar[A])
case class Bar[A](v: A)
val foo: Foo[_] = Foo[Int]("foo1", "Foo 1", Bar[Int](1))
foo.copy(id = "foo1.1")
But if I add another member of type Bar[A], it doesn't compile anymore:
case class Foo[A](id: String, name: String, v1: Bar[A], v2: Bar[A])
case class Bar[A](v: A)
val foo: Foo[_] = Foo[Int]("foo1", "Foo 1", Bar[Int](1), Bar[Int](2))
foo.copy(id = "foo1.1") // compile error, see below
type mismatch;
found : Playground.Bar[_$1]
required: Playground.Bar[Any]
Note: _$1 <: Any, but class Bar is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
Error occurred in an application involving default arguments
So far I found two workarounds:
- Make
Barcovariant inA, then the problem hides itself because nowBar[_$1] <: Bar[Any] - Define a
copyId(newId: String) = copy(id = newId)method onFooand call that instead, then we aren't callingcopyon a value of typeFoo[_].
However, neither of those are really feasible for my use case, Bar should be invariant, and I have too many different copy calls on Foo[_] instances to make copyThisAndThat methods for them all.
I guess my real question is, why is Scala behaving this way? Seems like a bug tbh.
idorname. I usually don't have any information available to me about the type param in such cases, thus the existential type at call site. But I do need the type param in other contexts when actually working withv1andv2members. - NikitaFoo[_]) those are tricky and will be reworked in Scala 3. One entity can be composed of may entities, it is really not that different for saying your case class has n fields. Also, you mentioned that you have a long code path were you do not care about the other fields, that is a good indication that you actually have more entities than one. Anyways, I just proposed a simple workaround, if you prefer to do a strange pattern match everywhere go ahead :) - Luis Miguel Mejía Suárez