对存在类型的Scala类型推断
考虑下面的代码片段,这是我的原始问题的简化版本:
case class RandomVariable[A](values: List[A])
case class Assignment[A](variable: RandomVariable[A], value: A)
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[Assignment[_]]): Double =
vars match {
case variable :: tail =>
val enumerated = for {value <- variable.values
extendedEvidence = evidence :+ Assignment(variable, value)
} yield enumerateAll(tail, extendedEvidence)
enumerated.sum
case Nil => 1.0
}
这会导致编译时错误,即当Assignment
所需类型Any
时,该variable
被推断为具有类型RandomVariable[_0]
。 为什么 value
不是也推断有类型 _0
? 我尝试给存储类型一个名称,以便通过使用case (variable: RandomVariable[T forSome {type T}]) :: tail =>
给编译器一个提示,但那也不会编译(说它找不到T型,我也会对这个解释感兴趣)。
为了进一步激励,请考虑我们何时捕获类型参数,如下所示:
case variable :: tail =>
def sum[A](variable: RandomVariable[A]): Double = {
val enumerated = for {value <- variable.values
extendedEvidence = evidence :+ Assignment(variable, value)
} yield enumerateAll(tail, extendedEvidence)
enumerated.sum
}
sum(variable)
这编译没有警告/错误。 有没有什么我可以修改在第一个例子不需要这个额外的功能?
编辑 :为了更明确,我想知道为什么value
不会被推断为类型_0
即使variable
类型为_0
,每个值都来自variable
的List[_0]
。 另外,我想知道是否有任何其他方法可以告诉编译器这个事实(除了在上面给出的功能中捕获类型)之外。
另一种编译解决方案比使用函数捕获类型更清晰(?)。 然而,它使得它更令人困惑,为什么类型推断在原始案例中失败。
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[SingleAssignment[_]]): Double = vars match {
case (variable@RandomVariable(values)) :: tail =>
val enumeration = for {value <- values
assignment = SingleAssignment(variable, value)
extendedEvidence = evidence :+ assignment
} yield enumerateAll(tail, extendedEvidence)
enumeration.sum
case Nil => 1.0
}
它还会返回以下警告:
scala: match may not be exhaustive.
It would fail on the following input: List((x: questions.RandomVariable[?] forSome x not in questions.RandomVariable[?]))
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[SingleAssignment[_]]): Double = vars match {
本文无法解读。 另外,使用一些测试用例运行它可以在参数列表中使用int,double和string的RandomVariable
s产生所需的结果,而不会出现匹配错误。
你不应该将RandomVariable和Assignment的类型绑定在一起吗?
def [A] enumerateAll(vars: List[RandomVariable[A]], evidence: List[Assignment[A]]): Double =
其实,你可以更宽容,只是说
def [A] enumerateAll(vars: List[RandomVariable[A]], evidence: List[Assignment[_ <: A]]): Double =
错误代码给出了解决方案的一些指示。
<console>:15: error: type mismatch;
found : RandomVariable[_0] where type _0
required: RandomVariable[Any]
Note: _0 <: Any, but class RandomVariable is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
extendedEvidence = evidence :+ Assignment(variable, value)
它告诉你,它看到了一个比它推断的更具体的类型,甚至建议让RandomVariable允许协变A.这将允许它在需要时向下改变类型。
case class RandomVariable[+A](values: List[A])
或者,您可以明确地在enumerateAll中为两个参数设置泛型类型。 以这种方式,它可以推断出适当的类型,而不是被迫推断Any。 这个定义不需要RandomVariable协变变化,因为两个参数都是相同的类型。
def enumerateAll[A](vars: List[RandomVariable[A]], evidence: List[Assignment[A]]): Double =
这个问题可能有助于解释。 为什么这个例子没有编译,又如何(合作,反对和in)方差工作?
链接地址: http://www.djcxy.com/p/38015.html