快速提问:为什么以下内容无法编译:
trait SubtypeOf[+T] {
type Type <: T
}
def SubtypeOf[T] :SubtypeOf[T] = new SubtypeOf[T] { type Type = T }
case class Owned[+P<:Owner](next :Option[P#R])
class Owner {
type This = this.type
val R = SubtypeOf[Owned[This]]
type R = R.Type
def next(o :Owned[This]) :Option[R] = o.next
}
结果:
Error:(23, 43) type mismatch;
found : Option[_2.R.Type forSome { val _2: Owner.this.This }]
(which expands to) Option[_2.R.Type forSome { val _2: Owner.this.type }]
required: Option[Owner.this.R]
(which expands to) Option[Owner.this.R.Type]
def next(o :Owned[This]) :Option[R] = o.next
这当然是我能想出的错误最简化的方法,背后的动机和长期问题如下:
我正在实现一个以json模式给出的协议,该模式由几个彼此嵌套的类组成。尽管该协议是一种标准,但不同的合作伙伴实施了自己的扩展,并且这些合作伙伴的数量也在增长。让我们说标准指定了一个'人'对象,可以按如下方式实现:
case class Person(firstName :String, familyName :String)
典型的扩展类似于以下内容:
class PersonP1(firstName :String, familyName :String, val birthDay :Date)
问题开始显示“人”对象是否嵌入数据树中的各种其他对象中,例如:root.history.transactions.buyer,root.history.transactions.buyer.spouse,root。 company.technicalContact,root.company.businessContact等 - 你明白了。在这种情况下,有两种类似的解决方案:
仅包含实际扩展数据的子类 - person,并且使用强制转换将所有代码与其一起使用。事实上,实际的业务逻辑隐含地要求某些对象具有特定的子类,而这是不可能强制实施的,也是一个难以记录的地狱。
子类递归所有引用扩展实体的类,即根路径上的所有对象。实际上,这需要子类大部分/整个模式的很大一部分,通常只需缩小其中一个属性的类型。
访客模式,略好于1)但仍然不允许将整个数据声明为特定于一个伙伴。
我选择了解决方案4:
定义容器类型P,声明要用于给定伙伴的实际类型,并使用P参数化所有实体。所以,而不是
case class Company(businessContact :Person, technicalContact :Person)
我有
trait Protocol {
type Person <: BasePerson
...
}
case class Company[P<:Protocol](businessContact :P#Person, technicalContact :P#Person)
这样做更好,但仍然每个协议都必须提供Protocol声明的所有数十种类型的定义,因为scala不允许覆盖类型声明。
所以,我试图通过引入上面例子中的协变类型持有者来规避这个限制:
trait SubtypeOf[+T] { type Type <: T }
trait Protocol {
type This = this.type
val Person :SubtypeOf[BasePerson[This]] = new SubtypeOf[BasePerson[This]] {
type Type = BasePerson[This]
}
type Person = Person.Type
....
}
object ExtendedProtocol extends Protocol {
override val Person = new SubtypeOf[ExtendedPerson[This]] { type Type = ExtendedPerson[This] }
}
这正是我想要的:当使用带有具体Protocol类型参数化的实体时,对象的所有成员都正确地显示为特殊的子类,我只需要覆盖实际更改的少数几个。如果我将实体类的所有引用限制为Protocol声明的直接引用的成员类型,那么一切都会好的。在选项[P#Person]或Seq [P#Person]中包装P#Person的任何尝试都会导致帖子开头出错。
从类型系统的角度来看,有一件事是参数化协议与所有实体类型,但它们太多了。我也尝试了
的自我参数化技巧trait Protocol[P<:Protocol[P]] { this :P =>
val Person = SubtypeOf[Person[P]]
type Person = Person.Type
}
但这打破了P#Person是一个路径依赖类型,因此例如person.address与
中的ProprietaryProtocol.Address不兼容。case class Person[P<:Protocol](address :P#Address)
val person :Person[ProprietaryProtocol]
object ProprietaryProtocol extends Protocol { ...
}