Понимание взаимодействия в Scala между самотипами и границами типов - PullRequest
2 голосов
/ 27 июля 2011

Ранее я пытался разбить эту проблему на более мелкие, более простые проблемы здесь и здесь , но я понял, что ответы на них, хотя и технически правильные, не помогают мне понять эту конкретную проблемуcase.

Я использую библиотеку 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]

Это работает из-за неявного представления, которое находится в-scope в 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)
}

Теперь мне нужен неявный в области действия всякий раз, когда я определяю новые записи, но я не хочу писать оператор импорта каждый раз:

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

И я не хочу вводить это неявно в любые другие области, кроме определений записей.

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

Итак, одна идея состоит в том, чтобы создать подкласс Record (или ввести mixin),затем определите мои записи схемы, расширив MyRecord вместо Record (или всегда микшируя в MyMixin).

class User extends MyRecord[Int, User] { ... }

Сначала я попробовал:

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

Мне любопытно понять, что только что произошло здесь, а также, возможно, еще несколько примеров, которые помогут мне лучше обернуть голову вокруг вещейтак что я не чувствую, что я всегда "возиться доэто работает. "

Извините за заголовок вопроса - не уверен, что это имеет какой-либо смысл.

1 Ответ

4 голосов
/ 28 июля 2011

Ваша первая ошибка проще и легко решается вашим окончательным решением.Тип self 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] extends Relation[K,R]

Вы определяете отношение в классе User какобъект User, который является Table[Int, User], следовательно, Relation[Int, User].

Тем не менее, ваш класс User является MyRecord[Int, User], но это означает, что это Record[Int, MyRecord[Int, User]], а не Record[Int, User].R из Record (то, что здесь имеет значение) - MyRecord[Int, User], а не User.таким образом, отношение должно быть Relation[Int, MyRecord[Int, User]].

A Relation[Int, User] не является Relation[Int, MyRecord[Int, User]], даже когда User является MyRecord[Int, User].В общем, если B является A, C[B] не является C[A], за исключением случаев, когда класс C заявляет об этом, будучи объявленным C[+X] вместо C[X].(следовательно, сообщение о том, что Relation является инвариантом (нет +), и предлагает +R, чтобы оно было ковариантным в R).

Я действительно менее уверен в ошибке в DefinitionHelper.Похоже, что R снова MyRecord[Int, User].Если вы явно укажете, что в качестве универсального параметра R, делая

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

, он должен работать (я сделал это на примере, очень близком к вашему коду, но на самом деле не использующ кругом).Почему компилятор этого не делает, я не знаю.В любом случае, тот факт, что ваш User не является Record[Int, User], но Record[Int, MyRecord[Int, User]], неизбежно вызывает проблемы.Фактическое решение намного проще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...