为什么Scala不能在此示例中推断出类型参数?

时间:2009-07-26 19:07:05

标签: generics scala types type-inference

假设我有两个类InputOutput,它们旨在相互连接。 Output生成某种类型的值,Input会消耗它们。

class Input[T] {
  var output: Option[Output[_ <: T]] = None
}
class Output[T] {
  var input: Option[Input[_ >: T]] = None
}

只要InputOutput对不能在相同类型的值上运行,只要Input类型参数是Output的超类型就可以了类型参数。请注意,两个类中的type参数都是不变的;在实际版本中,它用于共同和逆变位置。

我在其他地方有connect方法,可在Input / Output对之间建立链接:

def connect[T](output: Output[T], input: Input[_ >: T]) = {
  output.input = Some(input)
  input.output = Some(output)
}

如果我按如下方式调用此方法,则会出现类型错误:

val out = new Output[String]
val in = new Input[AnyRef]
connect(out, in)

错误是:

test.scala:17: error: type mismatch;
 found   : Output[String]
 required: Output[AnyRef]
  connect(out, in)
          ^

我可以通过写出类型参数来解决这个问题(在这种情况下,我会编写connect[String],但我认为编译器应该能够解决这个问题。如何更改{{1方法,以便自动推断类型参数?


修改:目前,我已将connect作为connect的方法,因此它会自动获取类型参数。这也有额外的好处,我可以使用中缀符号Output,但设计感觉有点尴尬。

我仍然对编译器出现这种行为的原因感兴趣。我觉得它应该能够推断出类型参数。这实际上是按指定的方式工作吗?

3 个答案:

答案 0 :(得分:6)

如果使用多个参数列表,有时会得到更好的结果:

def connect[T](output: Output[T])(input: Input[_ >: T]) = {
  output.input = Some(input)
  input.output = Some(output)
}

connect(out)(in)

......在这种情况下,确实有效。

答案 1 :(得分:0)

我可能完全错了,但我认为问题在于你将输入和输出联系起来: 输入的输出限制为T的子类型,但输出的输入限制为超类型T,唯一能满足这两个条件的类型是T.

Input[T] -> Output[ _ <: T ]
Output[Q] -> Input[ _ >: Q ]

使用输出创建输入(用_&lt;:T替换Q)时,您得到:

Input[T]->Input[ _ >: [_ <: T] ]

相同

输入[T] - &gt;输入[_&lt ;: T&lt;:_]

因此类型不匹配

答案 2 :(得分:0)

实际上scala类型inferene现在无法处理“递归”。因此,如果参数的类型只能在与其他参数的搭配中进行推理,则scala无法推断。但是如果您使用不同的参数列表,则scala f(a)(b)(c,d)将按列表推断类型,因此它通常可以更好地工作。

PS它过于简单了,但可以为你提供一些线索。