对存在类型的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 ,每个值都来自variableList[_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

上一篇: Scala type inference on an existential type

下一篇: Type Mismatch on Scala For Comprehension