Scala 类型参数作为注入映射



有一些类型T和抽象类X[T],重点是,对于每个具体的类型T,如果定义,只有一个X[T]子类,例如IntX extends X[Int],这是T = IntX[T]的唯一子类。也就是说,从理论上讲,我们有一个针对某些类型的单射映射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.plus1IntX并返回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技术?

如果每个T总是只有一个具体的子类Repr <: X[T],那么这个类Repr本身就会知道每隔一个X[T]都必须Repr。因此,只需将类型Repr作为参数提供给X,这样它就可以在所有plusXYZ-method声明中使用它:

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的功能是什么。它似乎什么也没做。

最新更新