[英]Scala case class copy doesn't always work with `_` existential type
I'm trying to copy()
a Scala case class which has a type param.我正在尝试
copy()
具有类型参数的 Scala 案例 class 。 At the call site, the type of the value is Foo[_]
.在调用站点,值的类型是
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:但是如果我添加另一个
Bar[A]
类型的成员,它就不再编译了:
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:到目前为止,我找到了两种解决方法:
Bar
covariant in A
, then the problem hides itself because now Bar[_$1] <: Bar[Any]
Bar
在A
中协变,然后问题就隐藏了,因为现在Bar[_$1] <: Bar[Any]
copyId(newId: String) = copy(id = newId)
method on Foo
and call that instead, then we aren't calling copy
on a value of type Foo[_]
.copyId(newId: String) = copy(id = newId)
方法并调用它,然后我们不会对Foo
Foo[_]
类型的值调用copy
。 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.但是,对于我的用例来说,这些都不是真正可行的,
Bar
应该是不变的,而且我对Foo[_]
实例有太多不同的copy
调用,无法为它们创建copyThisAndThat
方法。
I guess my real question is, why is Scala behaving this way?我想我真正的问题是,为什么 Scala 会这样? Seems like a bug tbh.
似乎是一个错误。
After the compiler handles named and default parameters, the calls become编译器处理命名参数和默认参数后,调用变为
foo.copy("foo1.1", foo.name, foo.v1)
and和
foo.copy("foo1.1", foo.name, foo.v1, foo.v2)
respectively.分别。 Or, if you replace the parameters with types,
或者,如果您将参数替换为类型,
foo.copy[?](String, String, Bar[_])
and和
foo.copy[?](String, String, Bar[_], Bar[_])
?
is the type parameter of copy
which has to inferred.是必须推断的
copy
的类型参数。 In the first case the compiler basically says " ?
is the type parameter of Bar[_]
, even if I don't know what that is".在第一种情况下,编译器基本上说“
?
是Bar[_]
的类型参数,即使我不知道那是什么”。
In the second case the type parameters of two Bar[_]
must really be the same, but that information is lost by the time the compiler is inferring ?
在第二种情况下,两个
Bar[_]
的类型参数必须确实相同,但是在编译器推断时该信息会丢失?
; ; they are just
Bar[_]
, and not something like Bar[foo's unknown type parameter]
.它们只是
Bar[_]
,而不是Bar[foo's unknown type parameter]
东西。 So eg " ?
is the type parameter of first Bar[_]
, even if I don't know what that is" won't work because so far as the compiler knows, the second Bar[_]
could be different.因此,例如“
?
是第一个Bar[_]
的类型参数,即使我不知道那是什么”将不起作用,因为据编译器所知,第二个Bar[_]
可能不同。
It isn't a bug in the sense that it follows the language specification;从遵循语言规范的意义上说,这不是一个错误。 and changing the specification to allow this would take significant effort and make both it and the compiler more complicated.
并且更改规范以允许这样做将花费大量精力,并使规范和编译器都更加复杂。 It may not be a good trade-off for such a relatively rare case.
对于这种相对罕见的情况,这可能不是一个好的权衡。
Another workaround is to use type variable pattern to temporarily give a name to _
:另一种解决方法是使用类型变量模式临时为
_
命名:
foo match { case foo: Foo[a] => foo.copy(id = "foo1.1") }
The compiler now sees that foo.v1
and foo.v2
are both Bar[a]
and so the result of copy
is Foo[a]
.编译器现在看到
foo.v1
和foo.v2
都是Bar[a]
,所以copy
的结果是Foo[a]
。 After leaving the case
branch it becomes Foo[_]
.离开
case
分支后,它变为Foo[_]
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.