首页 文章

Scala类型参数作为内射映射

提问于
浏览
0

有一些类型 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]
    ...
}

The problem is:
带有 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 -casting不会失败,因为我们知道我们对注入的限制 . 可以使用模式匹配,但这是细节 .

这个解决方案的主要缺点是需要类代码重构:我们在业务逻辑部分中放置了一些混乱的转换 .

此解决方案是否有权使用?你有什么想法?


Edit: 在这种情况下有没有办法使用 Aux 技术?

1 回答

  • 0

    如果每个 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-bound-polymorphism特技都会变得相当讨厌 . 类型组合往往组成更好 .

    顺便说一下:我不确定上面代码片段中 Context 的功能是什么 . 它似乎没有做任何事情 .

相关问题