Ограничение операции путем сопоставления параметра типа с типом аргумента, зависящим от пути - PullRequest
6 голосов
/ 02 апреля 2011

Я хотел бы использовать систему типов Scala для ограничения операций в системе, где есть версионные ссылки на некоторые значения.Все это происходит в некотором транзакционном контексте Ctx, к которому прикреплен тип версии V.Теперь есть Factory для создания ссылочных переменных.Они создаются с прикрепленной к ним версией creation (параметр типа V1), соответствующей версии контекста, в котором была вызвана фабрика.

Теперь представьте, что некоторый код пытаетсяполучить доступ к этой ссылке в более поздней версии, которая использует другую Ctx.Чего я хочу добиться, так это того, что запрещено вызывать доступ к этому Ref в любой версии (Ctx * type * field type), которая не соответствует версии создания, но вам разрешено разрешатьссылка на некоторый механизм замещения, который возвращает новое представление Ref, к которому можно получить доступ в текущей версии.(это нормально, если substitute вызывается с недопустимым контекстом, например, тот, который старше Ref V1 - в этом случае может быть выдано исключение времени выполнения)

Вот мойпопытка:

trait Version

trait Ctx {
  type V <: Version
}

object Ref {
  implicit def access[C <: Ctx, R, T](r: R)(implicit c: C, view: R => Ref[C#V, T]): T =
    view(r).access(c)

  implicit def substitute[C <: Ctx, T](r: Ref[_ <: Version, T])
                                      (implicit c: C): Ref[C#V, T] = r.substitute(c)
}
trait Ref[V1 <: Version, T] {
  def access(implicit c: { type V = V1 }): T // ???
  def substitute[C <: Ctx](implicit c: C): Ref[C#V, T]
}

trait Factory {
  def makeRef[C <: Ctx, T](init: T)(implicit c: C): Ref[C#V, T]
}

И проблема в том, чтобы определить метод класса access таким образом, чтобы все это компилировалось, то есть составной объект access должен компилироваться, но в то же время, которое я не могу вызватьэтот метод класса access с any Ctx, только с тем, чья версия соответствует версии справочника.

Желательно без структурной типизации или чего-либо, что вызывает проблемы с производительностью.

Ответы [ 2 ]

0 голосов
/ 05 апреля 2011

К вашему сведению, и, чтобы закрыть вопрос, вот еще одна идея, которая мне нравится, потому что клиентский код довольно беспорядочный:

trait System[A <: Access[_]] {
  def in[T](v: Version)(fun: A => T): T
}

trait Access[Repr] {
  def version: Version
  def meld[R[_]](v: Version)(fun: Repr => Ref[_, R]): R[this.type]
}

trait Version

trait Ref[A, Repr[_]] {
  def sub[B](b: B): Repr[B]
}

object MyRef {
  def apply[A <: MyAccess](implicit a: A): MyRef[A] = new Impl[A](a)

  private class Impl[A](a: A) extends MyRef[A] {
    def sub[B](b: B) = new Impl[B](b)
    def schnuppi(implicit ev: A <:< MyAccess) = a.gagaism
  }
}
trait MyRef[A] extends Ref[A, MyRef] {
  // this is how we get MyAccess specific functionality
  // in here without getting trapped in more type parameters
  // in all the traits
  def schnuppi(implicit ev: A <:< MyAccess): Int
}

trait MyAccess extends Access[MyAccess] {
  var head: MyRef[this.type]
  var tail: MyRef[this.type]
  def gagaism: Int
}

def test(sys: System[MyAccess], v0: Version, v1: Version): Unit = {
  val v2 = sys.in(v0) { a => a.tail = a.meld(v1)(_.head); a.version }
  val a3 = sys.in(v2) { a => a }
  val (v4, a4) = sys.in(v1) { a =>
    a.head = a.head
    println(a.head.schnuppi) // yes!
    (a.version, a)
  }
  // a3.head = a4.head // forbidden
}
0 голосов
/ 02 апреля 2011

Кажется, работает следующее:

trait Version

trait Ctx[+V1 <: Version] {
  type V = V1
}

type AnyCtx   = Ctx[_ <: Version]
type AnyRf[T] = Ref[_ <: Version, T]

object Ref {
  implicit def access[C <: AnyCtx, R, T](r: R)(
    implicit c: C, view: R => Ref[C#V, T]): T = view(r).access(c)

  implicit def substitute[C <: AnyCtx, T](r: AnyRf[T])(implicit c: C): Ref[C#V, T] =
    r.substitute( c )
}
trait Ref[V1 <: Version, T] {
  def access(implicit c: Ctx[V1]): T
  def substitute[C <: AnyCtx](implicit c: C): Ref[C#V, T]
}

trait Factory {
  def makeVar[C <: AnyCtx, T](init: T)(implicit c: C): Ref[C#V, T]
}

// def shouldCompile1(r: AnyRf[String])(implicit c: AnyCtx): String = r

def shouldCompile2(r: AnyRf[String])(implicit c: AnyCtx): String = {
  val r1 = Ref.substitute(r)
  r1.access(c)
}

// def shouldFail(r: AnyRf[String])(implicit c: AnyCtx): String = r.access(c)

Итак, последующие вопросы

  1. почему мне нужна избыточность типа параметр для Ctx для достижения этой цели. Я ненавижу, что эти типа параметры накапливаются как кролики в моем коде.
  2. почему shouldCompile1 не компилируется & ndash; могу ли я получить результаты работы по плану?

EDIT:

Это неправильно тоже. Отклонение аннотации неверно. Потому что теперь следующие компиляции, хотя это не должно:

def versionStep(c: AnyCtx): AnyCtx = c // no importa

def shouldFail3[C <: AnyCtx](f: Factory, c: C): String = {
  val r    = f.makeVar("Hallo")(c)
  val c2   = versionStep(c)
  r.access(c2)
}
...