Параметр типа Scala как инъективное отображение - PullRequest
0 голосов
/ 11 июня 2018

Имеют некоторый тип T и абстрактный класс X[T], и каков главный смысл, для каждого конкретного типа T, если он определен, существует только один подкласс X[T], например, IntX extends X[Int], которыйявляется единственным подклассом для X[T] с T = Int.То есть теоретически у нас есть инъективное отображение T -> X[T] для некоторого набора типов.

Давайте посмотрим на два простых определения:

trait Context[T] {
    type XType <: X[T]
}

abstract class X[T] extends Context[T] {
    def plus1(that: XType): XType = ??? /* doesn't matter */
    def plus2(that: XType): XType = that plus1 this
    def sum(x1: XType, x2: XType): XType = x1.plus1(x2)
}

Здесь мы видим наше X[T] снекоторые методы.Чтобы иметь правильные конечные типы в конкретных унаследованных подклассах, я использую XType как тип подкласса, унаследованного от X[T].Например, вот так:

trait IntContext extends Context[Int] {
    type XType = IntX
}

class IntX extends X[Int] with IntContext

А затем метод IntX.plus1 принимает IntX и возвращает IntX, а не X[Int], так что это краткое объяснение довольно абстрактного примера.Context используется для хранения всей информации о типах и конструкторах ткани, связанных с каждым используемым типом T. Что ж, есть более содержательный пример Context, просто для правильного понимания вещей:

trait Context[V <: ArithmType[V]] { /* V such as Int, Double */
    type Point <: AbstractPoint[V]
    type Line  <: AbstractLine[V]
    type Rect  <: AbstractRect[V]
    ...
    def newPoint(x: V, y: V):          Point
    def newLine(v1: Point, v2: Point): Line
    def newRect(p: Point, w: V, h: V): Rect
    ...
    def pointCompanion: AbstractPoint.Companion[V]
    def lineCompanion:  AbstractLine.Companion[V]
    def rectCompanion:  AbstractRect.Companion[V]
    ...
}

Проблема:
Код с X[T] не скомпилируется.Конечно, если мы рассмотрим два последних метода, мы получим следующие ошибки:

Type mismatch, expected: that.XType, actual: X[T]
Type mismatch, expected: x1.XType, actual: X.this.XType

Мы увидим, что компилятор обрабатывает собственный тип каждого экземпляра переменной XType как отличающийся друг от друга.И это правильно, конечно, но компилятор не знает, что такое инъекция нашего наследования: для фиксированного типа T все XType значения типов одинаковы.

Как я могу реализовать такоелогика для преодоления этого?


Я разработал одно решение, но оно довольно грязное.Перепишите код:

trait Context[T] {
    type XType <: X[T]
    implicit def cast(x: X[T]): XType = x.asInstanceOf(XType)
}

abstract class X[T] extends Context[T] {
    def plus1(that: XType): XType = ??? /* doesn't matter */
    def plus2(that: XType): XType = that plus1 that.cast(this)
    def sum(x1: XType, x2: XType): XType = x1 plus1 x1.cast(x2)
}

Без неявного приведения методы будут выглядеть следующим образом:

def plus2(that: XType): XType = cast(that plus1 that.cast(this))
def sum(x1: XType, x2: XType): XType = cast(x1 plus1 x1.cast(x2))

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

Основным недостатком этого решения является необходимость рефакторинга кода класса: мы помещаем некоторые загромождающие броски в части бизнес-логики.

Делает ли этоРешение имеет право быть использованным?Какие у вас есть идеи?


Редактировать: Есть ли способ использовать технику Aux в этом случае?

1 Ответ

0 голосов
/ 11 июня 2018

Если для каждого T всегда существует только один конкретный подкласс Repr <: X[T], то этот класс Repr сам будет знать, что каждый другой X[T] должен быть Repr.Итак, просто укажите тип Repr в качестве аргумента для X, чтобы он мог использовать его во всех объявлениях plusXYZ -методов:

trait Context[T, Repr <: X[T, Repr]]

abstract class X[T, Repr <: X[T, Repr]] extends Context[T, Repr] {
    def plus1(that: Repr): Repr = ??? /* doesn't matter */
    def plus2(that: Repr): Repr = that plus1 that
    def sum(x1: Repr, x2: Repr): Repr = x1 plus1 x2
}

class IntX extends X[Int, IntX]

Пока это работает, предупреждающее слово: всеэти круглые трюки с f-ограниченным полиморфизмом имеют тенденцию становиться довольно неприятными довольно быстро.Классы типов имеют тенденцию составлять намного приятнее.

И, кстати: я не уверен, что функция Context в приведенном выше фрагменте кода.Кажется, он ничего не делает.

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