简体   繁体   English

存在类型的Scala类型推断

[英]Scala type inference on an existential type

Consider the following code snippet, which is a reduced version of my original problem: 请考虑以下代码段,它是我原始问题的简化版本:

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
  }

This fails with the compile-time error that variable was inferred to have type RandomVariable[_0] when Assignment required type Any . Assignment需要类型为Any时,这会导致编译时错误,即variable被推断为类型为RandomVariable[_0] Why is value not also inferred to have type _0 ? 为什么 value 不推断具有类型 _0 I tried giving the existential type a name in order to give a hint to the compiler by using case (variable: RandomVariable[T forSome {type T}]) :: tail => but that also would not compile (saying it could not find type T, which I'd be interested in an explanation for as well). 我尝试给存在类型一个名称,以便通过使用case (variable: RandomVariable[T forSome {type T}]) :: tail =>给编译器一个提示但是也不会编译(说它无法找到输入T,我也会对它的解释感兴趣)。

For further motivation, consider when we capture the type parameter as follows: 为了进一步的动机,请考虑我们何时捕获类型参数,如下所示:

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)

This compiles without warnings/errors. 这编译没有警告/错误。 Is there something I can modify in the first example to not require this extra function? 我可以在第一个例子中修改一些不需要这个额外功能的东西吗?

EDIT : To be more explicit, I want to know why value is not inferred to be of type _0 even though variable is of type _0 and every value comes from a List[_0] in variable . 编辑 :为了更明确,我想知道为什么value不推断为_0类型,即使variable的类型为_0并且每个值都来自variableList[_0] Also I would like to know if there are any additional ways to tell the compiler of this fact (aside from capturing the type in a function as I gave above). 另外我想知道是否还有其他方法可以告诉编译器这个事实(除了捕获函数中的类型,如上所述)。

Another compiling solution, that is cleaner(?) than using a function to capture the type. 另一种编译解决方案,比使用捕获类型的函数更清晰(?)。 However, it makes it even more puzzling as to why type inference fails in the original case. 然而,它使得为什么类型推断在原始情况下失败更令人费解。

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
}

It also returns the following warning: 它还会返回以下警告:

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 {

Which I'm unable to decipher as of this posting. 这个帖子我无法破译。 Also, running it with a few test cases produces the desired result without a match error using RandomVariable s of int, double, and string in the parameter list. 此外,使用一些测试用例运行它会在参数列表中使用int,double和string的RandomVariable s生成所需的结果,而不会出现匹配错误。

Shouldn't you bind the types of RandomVariable and Assignment together? 你不应该将RandomVariable和Assignment的类型绑定在一起吗?

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

actually, you can be more permissive and just say 实际上,你可以更宽容,只是说

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

The error code gives some indication of a solution. 错误代码给出了解决方案的一些指示。

<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)

It's telling you that it saw a more specific type than it inferred and even suggested to make RandomVariable allow a covariant A. This would allow it to vary the type downward when required. 它告诉你它看到了比推断更具体的类型,甚至建议使RandomVariable允许协变A.这将允许它在需要时向下改变类型。

case class RandomVariable[+A](values: List[A])

Alternatively, you can explicitly set the generic type in enumerateAll for both parameters. 或者,您可以在enumerateAll中为两个参数显式设置泛型类型。 In that way it can infer the appropriate type instead of being forced to infer Any. 以这种方式,它可以推断出合适的类型,而不是被迫推断任何。 This definition doesn't require the RandomVariable covariant change as both parameters are of the same type. 此定义不需要RandomVariable协变变化,因为两个参数属于同一类型。

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

This question may help with the explanation. 这个问题可能有助于解释。 Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work? 为什么示例不编译,又如何(共同,反向和反向)方差有效?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM