Scala:在路径依赖的上下文中重用由路径依赖的类型产生的泛型 [英] Scala: Re-use generic resulting from path-dependent type in path-dependent context
问题描述
简而言之:以下内容无法编译(原因如下),我该如何使其工作?
In short: The following does not compile (reason below), how can I make it work?
trait Simulator {
type CM[T]
def useCM(v: CM[_])
}
case class CMH[S <: Simulator,T](cm: S#CM[T])
class SimIterator[S <: Simulator](val sim: S, val cmhs: Seq[CMH[S,_]]) {
cmhs foreach { cmh => sim.useCM(cmh.cm) }
/*
compile time error:
type mismatch; found : cmh.cm.type (with underlying type S#CM[_$2]) required:
SimIterator.this.sim.CM[_] Note: _$2 <: Any (and cmh.cm.type <: S#CM[_$2]),
but type CM is invariant in type T. You may wish to define T as +T instead.
(SLS 4.5)
*/
}
结构背后的想法是, CMH
在 SimIterator
中隐藏特定于 T
的行为,后者由后者处理常见任务. S
用于强制 CMH
中的vlaues具有正确的类型,而没有 Simulator
的实例.
The idea behind the structure is that CMH
hides T
-specific behavior from SimIterator
where as the latter handles common tasks. S
is used to force vlaues in CMH
to have the right type without having an instance of Simulator
.
在 foreach
中,似乎存在与 CM
相关的子类型问题.如果 S#CM
是具体类型,则需要 sim.CM =:= S#CM
.但是,请查看以下内容:
In the foreach
, there seems to be a subtyping issue related to CM
. If S#CM
is a concrete type we need sim.CM =:= S#CM
. However, look at the following:
object Test extends Simulator {
type CM[T] = Option[T]
def useCM(v: CM[_]) = println(v)
def mkCM[T]: CM[T] = None
CMH[Simulator,AnyRef](mkCM[AnyRef])
}
我们现在有一个 CMH
,我们可以将其与任何 Simulator
一起传递到 SimIterator
中.因此,显然 SimIterator
的类型限制不够.
We have now a CMH
that we can pass into a SimIterator
together with any Simulator
. So apparently the typing of SimIterator
is not restrictive enough. How can express (and use) S =:= sim.type
?
更新
这有效,但是不能在构造函数中使用(非法的依赖方法类型:参数以同一节或更早的节中的另一个参数的类型出现)
This works, but cannot be used in the constructor (illegal dependent method type: parameter appears in the type of another parameter in the same section or an earlier one)
class SimIterator(val sim: Simulator) {
def doIt(cmhs: Seq[CMH[sim.type,_]]) {
cmhs foreach { cmh => sim.useCM(cmh.cm) }
}
}
上面的示例有效,但是不是我想要的. cmhs
应该在构造时传递.
The upper example works, but is not what I want. cmhs
should be passed-in upon construction.
推荐答案
您可以通过将抽象类型成员移动到这样的类型参数来轻松解决此问题:
You can easily fix the issue by moving the abstract type member to a type parameter like this:
trait Simulator[CM[_]] {
def useCM(v: CM[_])
}
case class CMH[CM[_]](cm: CM[_])
class SimIterator[S <: Simulator[CM], CM[_]](val sim: S, val cmhs: Seq[CMH[CM]]) {
cmhs foreach { cmh => sim.useCM(cmh.cm) }
}
这样可以避免使用类型投影,如果您确实需要让CM保持为类型成员,请提供更多详细信息,以说明在哪种情况下需要类型投影以及原因.
That way you avoid using the type projection, if you really need to keep CM as a type member please provide more detail explaining in which case you need a type projection and why.
编辑/更新
欢迎来到末日面包房"!;-)
Welcome to the "Bakery of Doom"! ;-)
在这种情况下,有两种解决方案:
There is two solution in this case:
首先,将迭代器放入蛋糕,以便您可以直接访问该类型:
1st, put you Iterator into the cake, so you can access the type directly:
case class CMH[CM[_]](cm: CM[_])
trait Cake {
type CM[_]
trait Simulator {
def useCM(v: CM[_])
}
class SimIterator[S <: Simulator](val sim: S, val cmhs: Seq[CMH[CM]]) {
cmhs foreach { cmh => sim.useCM(cmh.cm) }
}
}
或第二,将Iterator封装在另一个类中,从而可以访问与路径相关的类型:
or 2nd, encapuslate your Iterator in an other class, making possible to access the path-dependent type:
trait Cake {
type CM[_]
trait Simulator {
def useCM(v: CM[_])
}
}
case class CMH[CM[_]](cm: CM[_])
class SimIteratorBuilder[C <: Cake](val cake: Cake) {
class SimIterator(val sim: cake.Simulator, val cmhs: Seq[CMH[cake.CM]]) {
cmhs foreach { cmh => sim.useCM(cmh.cm) }
}
}
一旦进入蛋糕,就无法逃脱!
Once you are in the cake, there is no way to escape it!
这篇关于Scala:在路径依赖的上下文中重用由路径依赖的类型产生的泛型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!