存在类型的Scala类型推断

时间:2013-01-10 03:51:41

标签: scala types type-inference scala-2.10 existential-type

请考虑以下代码段,这是我原始问题的简化版本:

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
  }

variable需要RandomVariable[_0]类型Assignment时,Any被推断为类型为value的编译时错误。 为什么 _0 还没有推断出类型 case (variable: RandomVariable[T forSome {type T}]) :: tail => 我尝试按顺序为存在类型命名使用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) 给编译器一个提示,但也不会编译(说它找不到类型T,我也会对它的解释感兴趣)。

为了进一步激励,请考虑我们何时捕获类型参数:

value

此编译没有警告/错误。我可以在第一个例子中修改一些不需要这个额外功能的东西吗?

编辑:为了更明确,我想知道为什么_0未被推断为variable类型,即使_0的类型为List[_0] {1}}并且每个值都来自variable中的{{1}}。另外我想知道是否还有其他方法可以告诉编译器这个事实(除了在上面给出的函数中捕获类型)。

3 个答案:

答案 0 :(得分:1)

另一种编译解决方案,比使用函数捕获类型更清晰(?)。然而,在原始案例中,为什么类型推断失败更令人费解。

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生成所需的结果,而不会出现匹配错误。

答案 1 :(得分:0)

你不应该将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 = 

答案 2 :(得分:0)

错误代码给出了解决方案的一些指示。

<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中为两个参数显式设置泛型类型。以这种方式,它可以推断出合适的类型,而不是被迫推断任何。该定义不需要RandomVariable协变变化,因为两个参数属于同一类型。

def enumerateAll[A](vars: List[RandomVariable[A]], evidence: List[Assignment[A]]): Double = 

这个问题可能有助于解释。 Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work?