Scala推断的类型参数 - 类型范围推断为'Nothing'
我正在尝试编写一个简单的查询monad,并且遇到问题,我的通用类型注释正确。Scala推断的类型参数 - 类型范围推断为'Nothing'
我第一次尝试去如下(大大简化了简洁)
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
}
编译器不喜欢这一点,因为它无法推断“T”的类型。
error: inferred type arguments [People.type,Nothing] do not conform to method apply's type parameter bounds [U <: Schema[T],T]
虽然尝试我发现,使用视图的边界,而不是按预期工作
case class Query[U <% Schema[T], T](schema: U) {
(注意使用的角度约束“<%”,而不是约束型“<”)
然而,在我对类型系统有限的理解中,因为我期待Schema [T]的实际子类(而不仅仅是可转换性),所以我会假定类型绑定“<:”是在这里使用的正确界限?
如果是这样的话,我错过了什么 - 我怎么给编译器足够的提示,以便在使用类型边界而不是视图边界时正确推断T?
为了编码的两个类型参数之间的关系,你可以使用像
case class Query[U, T](schema: U)(implicit ev: U <:< Schema[T]) { ... }
参见4.3节和Scala Language Spec的§4.4获取更多信息。
我总是发现,一类/函数使用两种类型标识符时,类型推理系统不能如预期般工作,你必须要明确的,像这样:
val query = Query[People.type, Person](People)
如果你改变了你Query
声明如下:
case class Query[U <: Schema[_](schema: U)
你会能够做到这一点:
val query = Query(People)
但是,您不知道提供的Schema
的基础类型,并且将无法正确实现results
函数。
这不是一个完全统计的答案(至少对我来说),因为我不得不承认,我不能确切地说出这里的推理失败的原因和原因。我对此只有一些模糊的直觉。 该问题与编译器一次推断两个类型参数有关。 至于为什么改变绑定到视图边界的类型会修复编译,我的理解是现在有两个参数列表,结果我们现在有两个连续的类型推断阶段,而不是两次推断。事实上,以下内容:
case class Query[U <% Schema[T], T](schema: U)
是一样的:
case class Query[U, T](schema: U)(implicit conv: U => Schema[T])
第一个参数驱动器列表的U
推理,然后第二个(注意:U
现在知道)将推动推断T
。
在表达式Query(People)
的情况下,参数People
将驱动类型推理器将U
设置为People.type
。然后,编译器将查找从People.type
到Schema[T]
的隐式转换,以传入第二个参数列表。范围中的唯一一个是从People.type
到Schema[Person]
的(微不足道的)转换,从而推断推导出T = Person
。
要修复编译而不诉诸约束视图,你可以用一个抽象类型取代类型参数T
:
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)
}
UPDATE:
@Aaron Novstrup的: 据我所知,你的回答是不正确的(更新更新:来自Aaron的原始答案声称Query
声明等于case class Query[U <: Schema[X], T](schema: U)
)。
case class Query[U <: Schema[X], T](schema: U)
甚至没有编译。 比方说,你的意思是
case class Query[U <: Schema[_], T](schema: U)
(其中确实编译),很容易在REPL检查,这是不一样的两种。
事实上,以下罚款编译:
case class Query[U <: Schema[_], T](schema: U)
type MyQuery = Query[Schema[String], Int]
虽然,以下不会:
case class Query[U <: Schema[T], T](schema: U)
type MyQuery = Query[Schema[String], Int]
因此证明了差异。错误是:
<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]
明确显示的T
第一和第二OCCURENCES表示相同类型的,我们做有两种类型的参数之间的关系。
我有同样的问题。以下为我工作:
case class Query[U <: Schema[T], T](schema: U with Schema[T]) {
...
}
谢谢。我曾经看到过这种语法,并在想这是什么意思。 – 2013-05-01 06:36:24
我不认为这个答案是正确的。请在我的答案底部查看我的回复(此评论不会为讨论提供足够的空间)。 – 2013-05-01 09:01:01
@Régis你是对的。我误解了规范中的范围规则。这确实是一个推理问题,正如你在答案中所描述的那样。也就是说,使用隐式证据参数是对关系进行编码并支持所需推理的最简单方法。 – 2013-05-01 19:51:11