Подтвердить что ты не робот

Scala Модификаторы и параметризация типа

Я создаю класс memoization.

Каждый класс запоминает тип функции и имеет следующее определение:

 class MemoizedFunction1[-T1, +R](f: T1 => R) {
    private[this] val cache = mutable.Map[T1, R]()
    def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))
  }

Это компилируется красиво и работает так, как ожидалось. Однако, если я удаляю измененный private[this], я получаю следующую ошибку:

contravariant type T1 occurs in invariant position in type => scala.collection.mutable.Map[T1,R] of value cache

Почему это, когда я удаляю модификатор, вдруг контравариантный тип T1 вмешивается в инвариантный тип Карты? Как модификаторы влияют на параметризацию типа?

4b9b3361

Ответ 1

Не то, чтобы я все это понял, но это описано в разделе 4.5 (вариации аннотации) Scala Language Specification 2.9 on страница 45

Ссылки на параметры типа в объектно-частных или объектно-защищенных значениях, переменных или методах (§5.2) класса не проверяются для их позиции дисперсии. В эти члены параметр типа может появляться где угодно, не ограничивая его юридические аннотации на отклонения.

Чтобы упростить ваш пример, согласно спецификации, это нормально:

class Inv[T]

class Foo[-T] {
  private[this]   val a: Inv[T] = sys.error("compiles")
  protected[this] val b: Inv[T] = sys.error("compiles")
}

Но если вы удалите [this], он будет жаловаться. На некотором уровне это имеет смысл, поскольку, если он не является частным или защищенным объектом, контравариантный тип возврата может протекать вне объекта и вызывать ошибку времени выполнения.

Ответ 2

Предположим, вы можете удалить [this].

Без [this] вы можете добавить метод getOtherCache:

class MemoizedFunction1[-T1, +R](f: T1 => R) { 
  private val cache = mutable.Map[T1, R]() // trait Map[A, B] extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
  def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))

  def getOtherCache(other: MemoizedFunction1[T1, R]) {
    val otherCache: mutable.Map[T1, R] = other.cache;
  }
}

class A
class B extends A

val mf1: MemoizedFunction1[B, B] = new MemoizedFunction1[B, B](b => b)

val mf2: MemoizedFunction1[B, B] = new MemoizedFunction1[A, B](a => new B)
// mf2 is MemoizedFunction1[B, B]
// mf2 contains mutable.Map[A, B]

mf1.getOtherCache(mf2) //Error! mf2.cache is NOT mutable.Map[B, B]!

Ответ 3

Программирование в Scala затрагивает эту тему в разделе 19.7 Object частные данные: "Доступ к частным частям объекта возможен только из объекта, в котором они определены. Получается, что обращения к переменным из того же объекта, в котором они определены, не создают проблем с дисперсией".