Зависимый тип с обратной ссылкой (Scala)

Я играю с зависимыми (от пути) типами в Scala и наткнулся на следующий сценарий, для которого я не могу найти хорошего решения. Предположим, я хочу иметь некоторую иерархию зависимых типов, и я хочу, чтобы каждый из них имел обратную ссылку на свой объект «владелец». Я хочу, чтобы эта обратная ссылка могла вызывать некоторые методы для правильных объектов-«владельцев». Как правильно это делать?

Вот небольшой пример. Есть «базовая» черта Outer с зависимым типом Inner. Базовая черта Outer определяет некоторый метод double, который работает с зависимым типом. Существует также особый класс ConcreteOuter с определенным зависимым классом ConcreteInner, который использует простой Int для значения.

trait Outer {
  outerSelf =>

  trait BaseInner {
    val outer: outerSelf.type = outerSelf

    def asDependent: outer.Inner         // #1
    //  def asDependent: outerSelf.Inner // #2
  }

  type Inner <: BaseInner

  def double(inner: Inner): Inner
}

class ConcreteOuter extends Outer {
  case class ConcreteInner(val v: Int) extends BaseInner {
    override def asDependent = this    
  }

  type Inner = ConcreteInner

  def createInner(v: Int): Inner = new ConcreteInner(v)

  override def double(inner: Inner): Inner = new ConcreteInner(2 * inner.v)
}

Все идет нормально. Теперь предположим, что я хотел бы иметь возможность вызывать этот double метод в контексте, где у меня есть только экземпляр некоторого Inner класса, но не соответствующий Outer-экземпляр. Например, давайте попробуем создать другой double метод, который просто вызывает исходный Outer.double в каком-то другом (независимом) контексте:

object DepTest extends App {

  //def double(inner: Outer#Inner) = inner.outer.double(inner)           // #3

  def double(inner: Outer#Inner) = inner.outer.double(inner.asDependent) // #4


  val c1 = new ConcreteOuter
  val i1 = c1.createInner(123)
  val d1 = double(i1)
  println(d1)

}

Этот код компилируется, но требует довольно уродливого взлома asDependent. Если я использую строку №3 вместо строки №4, код не компилируется. Если я разделю строку №3 следующим образом, код больше не будет компилироваться

  def double(inner: Outer#Inner) = {
    val outer = inner.outer
    outer.double(inner.asDependent)
  }

Более того, если я заменю строку №1 строкой №2, даже взлом asDependent перестанет работать.

Таким образом, иногда кажется, что компилятор каким-то образом знает, что поле outer объекта Inner и объект «владелец», также известный как outerSelf, - это одно и то же, а иногда это не так, и непонятно, как убедить компилятор, если он не признать их одним и тем же.

Есть ли способ обойти это? Или это совершенно неправильный подход к моей проблеме? (Очевидно, что в реальном мире я хотел бы создать не просто тупые прокси, такие как DepTest.double, но некоторую библиотеку функций более высокого уровня, например multiplyByPow2(val : Outer#Inner, exponent: Int))


person SergGr    schedule 25.02.2017    source источник


Ответы (1)


Я не очень разбираюсь в типах, зависящих от пути, но из того, что я могу прочитать из здесь, кажется, что происходит следующее: Outer и outerSelf.type не одно и то же:

  • outerSelf.type состоит из outerSelf или null
  • outerSelf: Outer состоит только из outerSelf

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

person alamit    schedule 25.02.2017