理解Scala中自我类型和类型边界之间的交互



我以前试着把这个问题分成更小、更简单的问题,但我意识到,这些问题的答案虽然在技术上是正确的,但并不能帮助我理解这个特殊的情况。

我正在使用一个库,Circumflex ORM,它允许您定义模式如下:

class User extends Record[Int, User] {
  val name = "name".TEXT
  val age = "age".INTEGER
  def relation = User
}
object User extends User with Table[Int, User]

这样做是因为在Record:

范围内有一个隐式视图。
abstract class Record[PK, R <: Record[PK, R]] extends Equals { this: R =>
  implicit def view(x: String) = new DefinitionHelper(x, this)
  ...
}
class DefinitionHelper[R <: Record[_, R]](name: String, record: R) {
  def TEXT = ...
  def INTEGER = ...
  ...
}

我正在尝试在TEXT等之外引入一种新的扩展方法,称为BYTEA。所以我知道我需要我自己的隐式助手类:

class DefinitionHelper[R <: Record[_, R]](name: String, record: R) {
 def BYTEA = new BytesField[R](name, record)
}

现在我需要一个隐式的作用域每当我定义新的记录,但是我不想每次都写一个import语句:

class User extends Record[Int, User] {
 import Implicits._
 ...
}

我不想把这个隐式引入任何其他作用域除了记录定义之外。

import Implicits._
class User extends Record[Int, User] { ... }
所以一个想法是子类化Record(或引入mixin),然后定义我的模式记录通过扩展MyRecord而不是Record(或总是)
class User extends MyRecord[Int, User] { ... }

I first try:

abstract class MyRecord[PK, R <: MyRecord[PK, R]]
extends Record[PK, R] {
  implicit def str2ddlHelper2(str: String) =
    new DefinitionHelper(str, this)
}

这产生:

illegal inheritance;  self-type MyRecord[PK,R] does not conform to ru.circumflex.orm.Record[PK,R]'s selftype R

所以我试着:

abstract class MyRecord[PK, R <: MyRecord[PK, R]]
extends Record[PK, MyRecord[PK, R]] {
 implicit def str2ddlHelper2(str: String) =
   new DefinitionHelper(str, this)
}

但是在定义记录时,我得到了这两个问题:

class User extends MyRecord[Int, User] {
 val id = "id".INTEGER
 val value = "value".BYTEA // works
 val value2 = new DefinitionHelper("", this) // does not work?!
 ...
 def relation = User // another error here
}
object User extends User with Table[Int, User]

错误如下:

inferred type arguments [User] do not
conform to class DefinitionHelper's type parameter bounds [R <:
ru.circumflex.orm.Record[_, R]]
type mismatch;  found   : User.type (with
underlying type object User)  required:
ru.circumflex.orm.Relation[Int,MyRecord[Int,User]]
Note: User <:
MyRecord[Int,User] (and
User.type <:
ru.circumflex.orm.Table[Int,User]), but
trait Relation is invariant in type R. You may wish to define R as +R
instead. (SLS 4.5)

在更多的摆弄之后,我惊讶地发现了一些有用的东西:

abstract class MyRecord[PK, R <: MyRecord[PK, R]]
extends Record[PK, R] { this: R =>
  implicit def str2ddlHelper2(str: String) =
    new DefinitionHelper(str, this)
}

我很想知道刚才发生了什么,也许还有一些例子可以帮助我更好地理解事情,这样我就不会觉得我总是在"摆弄它直到它起作用"。"

为问题标题道歉-不确定它是否有任何意义。

第一个错误比较简单,通过最终解决方案可以轻松解决。声明

中的自我类型R=>
Record[PK, R <: Record[PK, R]] extends Equals { this: R =>

强制Record的每个后代确保它也将是R(它不会使它成为R,后代必须做一些事情才能成为R)。在实践中,这意味着在class X extends Record[PK, R]中,R必须是X的祖先(并且还有R <: Record[PK, R],大多数时候应该是X,但正如我们将在最后看到的那样,情况可能并非如此)。

这个约束在MyRecord中消失了,因此出现了第一个错误。你的最终解再次陈述了约束条件,这就是它的结束。


你的第二个版本更复杂。我从第二个错误开始。

首先,介绍一些未在上面说明的来自Circumflex API的元素。

  • Record有一个摘要def relation: Relation[PK, R]
  • Table[K,R]扩展Relation[K,R]

在类User中将关系定义为对象User,它是Table[Int, User],因此是Relation[Int, User]

然而,你的类UserMyRecord[Int, User],但这意味着它是Record[Int, MyRecord[Int, User]],而不是Record[Int, User]RecordR(这里重要的一个)是MyRecord[Int, User],而不是User。因此relation必须是Relation[Int, MyRecord[Int, User]]

即使UserMyRecord[Int, User], Relation[Int, User]也不是Relation[Int, MyRecord[Int, User]]。一般来说,如果BA,那么C[B]就不是C[A],除非C类声明为C[+X]而不是C[X]。(因此关于Relation不变 (no +)的消息,并建议+RR中是协变)。

我真的不太确定在DefinitionHelper的错误。这似乎又和R是MyRecord[Int, User]有关。如果您显式地将其声明为通用参数R,则执行

new DefinitionHelper[MyRecord[Int, User]]("", this) 

应该可以工作(我在一个非常接近您的代码的示例中这样做了,但实际上没有使用circumflex)。为什么编译器不推断,我不知道。无论如何,您的User不是Record[Int, User],而是Record[Int, MyRecord[Int, User]],这一事实必然会引起问题。实际的解决方案要简单得多。

最新更新