F-ограниченные типы и методы с параметрами типов на сайтах аргументов и возврата

У меня есть F-ограниченный тип, и моя цель - создать метод с параметризацией типа, чтобы иметь возможность использовать его повторно. Вот пример кода:

trait FType {
  type ThisType <: FType

  def deepCopy(): ThisType

}

class ConcreteType extends FType {

  override type ThisType = ConcreteType

  override def deepCopy(): ConcreteType = this

}

class ConcreteType2 extends FType {

  override type ThisType = ConcreteType2

  override def deepCopy(): ConcreteType2 = this

}

object FType {

  def deepCopy[T <: FType](_type: T): T = {
    _type.deepCopy()
  }

/*  def deepCopy2(c: ConcreteType2): ConcreteType2 = {
    c.deepCopy()
  }*/

  def main(args: Array[String]): Unit = {
    //deepCopy2(new ConcreteType2)
  }

}

Тем не менее, код не компилируется. Компилятор выдает эту ошибку:

Error:(29, 19) type mismatch;
 found   : _type.ThisType
 required: T
    _type.deepCopy()

Я понимаю, что это имеет отношение к зависимым от пути типам, так как _type.ThisType не тот же тип, что и T,

Но как же тогда я могу воспользоваться F-ограниченными типами, если F-ограниченный тип не совсем совпадает с типом, использующим F-ограниченный тип? Если типы не совпадают, как это deepCopy2 компилирует?

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

2 ответа

Если типы не совсем одинаковы, как компилируется этот deepCopy2?

Это довольно просто. Это работает, потому что нет вовлеченного полиморфизма. Статически известно, что ConcreteType2 имеет deepCopy() метод, который возвращает ConcreteType2, Вы могли бы даже удалить type ThisType из всей иерархии, и это все равно будет работать так же.


Но как же тогда я могу воспользоваться F-ограниченными типами, если F-ограниченный тип не совсем совпадает с типом, использующим F-ограниченный тип?

Вы должны сказать компилятору, что он такой же, как вы не указали достаточно. Давайте посмотрим на пример, который работает и является полиморфным:

def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
//                      ^ --important bit-- ^

Это определяет метод, который работает для любого A то есть FType а также это член типа ThisType установлен в Aсвязывая их вместе. Это означает, что это будет работать для ваших определений ConcreteType а также ConcreteType2, Однако он НЕ будет компилироваться для классов, которые не определены должным образом, таких как этот:

class Bogus extends FType {
  override type ThisType = ConcreteType2
  override def deepCopy(): ConcreteType2 = new ConcreteType2
}

deepCopy(new Bogus)

В качестве альтернативы, давайте начнем с немного модифицированной версии вашего метода:

def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
                                   ^path-dependent^

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

val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature

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

def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()

Это, опять же, запрещает вызов с Bogus

В F-ограниченных типах у вас обычно ThisType в качестве параметра типа вместо члена типа:

trait FType[ThisType <: FType] {
  def deepCopy(): ThisType    
}

class ConcreteType extends FType[ConcreteType] {
  override def deepCopy(): ConcreteType = this
}

// in object FType
def deepCopy[T <: FType[T]](_type: T): T = {
  _type.deepCopy()
}

Обратите внимание на разницу: в FType.deepCopy компилятор знает, что тип возвращаемого значения _type.deepCopy() является T,

Вы можете сделать то же самое с членами типа:

def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
  _type.deepCopy()
Другие вопросы по тегам