Scala推断类型参数 - 类型边界推断为'Nothing'

Jam*_*ies 7 generics scala type-inference type-bounds

我正在尝试编写一个简单的查询monad,并且无法使我的泛型类型注释正确.

我的第一次尝试如下(为简洁而大大简化)

case class Person( val name: String )
abstract class Schema[T]    
object People extends Schema[Person]

case class Query[U <: Schema[T], T]( schema: U ) {      <---- Type signature
    def results: Seq[T] = ...
    def where( f: U => Operation ) = ...
}

class TypeText extends Application {
    val query = Query( People )                     <---- Type inference fails
}
Run Code Online (Sandbox Code Playgroud)

编译器不喜欢这样,因为它无法推断'T'的类型.

错误:推断类型参数[People.type,Nothing]不符合方法apply的类型参数bounds [U <:Schema [T],T]

在进行实验时,我发现使用视图边界可以按预期工作

case class Query[U <% Schema[T], T]( schema: U ) {
Run Code Online (Sandbox Code Playgroud)

(注意使用视图绑定"<%"而不是类型绑定"<:")

然而,由于我对类型系统的理解有限,因为我期待Schema [T]的实际子类(而不仅仅是可转换性),我会假设类型绑定"<:"是在这里使用的正确边界?

如果是这种情况,我错过了什么 - 在使用类型边界而不是视图边界时,如何给编译器足够的提示来正确推断T?

Rég*_*les 5

这不是一个完全令人满意的答案(至少对我而言),因为我不得不承认我无法确切地说明推理在这里失败的确切位置和原因.我对它只有一些模糊的直觉.该问题与编译器必须一次推断两个类型参数有关.至于为什么更改绑定到视图边界的类型会修复编译,我的理解是现在有两个参数列表,因此我们现在有两个连续的类型推断阶段,而不是一次两个推理.的确如下:

case class Query[U <% Schema[T], T]( schema: U )
Run Code Online (Sandbox Code Playgroud)

是相同的:

case class Query[U, T]( schema: U )( implicit conv: U => Schema[T] )
Run Code Online (Sandbox Code Playgroud)

第一个参数列表驱动推理U,然后第二个参数列表(U现在知道的注释)将推动推断T.

在表达式的情况下Query( People ),参数People将驱动类型推理器设置UPeople.type.然后,编译器将查找从People.typeto 的隐式转换Schema[T],以传入第二个参数列表.范围内唯一的一个是(平凡)转换People.typeSchema[Person],驱动推理器推导出来T = Person.

要在不诉诸视图边界的情况下修复编译,可以T使用抽象类型替换type参数:

case class Person( val name: String )
sealed trait Schema {
  type T
}
abstract class SchemaImpl[_T] extends Schema {
  type T = _T
}
object People extends SchemaImpl[Person]
case class Query[U <: Schema]( schema: U ) {
  def results: Seq[schema.T] = ???
}
class TypeText extends Application {
  val query = Query( People )
}
Run Code Online (Sandbox Code Playgroud)

更新:

@Aaron Novstrup:据我所知,你的答案是不正确的(更新更新:Aaron的原始答案声称Query声明是等同的case class Query[U <: Schema[X], T](schema: U)).

case class Query[U <: Schema[X], T](schema: U)
Run Code Online (Sandbox Code Playgroud)

甚至没有编译.让我们说你的意思

case class Query[U <: Schema[_], T](schema: U)
Run Code Online (Sandbox Code Playgroud)

(这编译),很容易在REPL检查,这是不一样的两种.

实际上,以下编译很好:

case class Query[U <: Schema[_], T](schema: U)
type MyQuery = Query[Schema[String], Int]
Run Code Online (Sandbox Code Playgroud)

虽然,以下不是:

case class Query[U <: Schema[T], T](schema: U)
type MyQuery = Query[Schema[String], Int]
Run Code Online (Sandbox Code Playgroud)

因此证明了差异.错误是:

<console>:10: error: type arguments [Schema[String],Int] do not conform to class Query's type parameter bounds [U <: Schema[T],T]
       type MyQuery = Query[Schema[String], Int]
Run Code Online (Sandbox Code Playgroud)

这清楚地表明第一次和第二次出现T表示相同的类型,并且我们确实在两个类型参数之间存在关系.


Aar*_*rup 3

为了对两个类型参数之间的关系进行编码,您可以使用类似的东西

\n\n
case class Query[U, T](schema: U)(implicit ev: U <:< Schema[T]) { ... }\n
Run Code Online (Sandbox Code Playgroud)\n\n

有关详细信息,请参阅Scala 语言规范的 \xc2\xa74.3 和 \xc2\xa74.4 。

\n