Scala 不可变类的多态更新
我一直在想方设法解决这个问题,但似乎找不到解决这个问题的办法。我似乎无法在Scala中对其进行正确建模 假设我有一个traitScala 不可变类的多态更新,scala,inheritance,polymorphism,abstract-type,Scala,Inheritance,Polymorphism,Abstract Type,我一直在想方设法解决这个问题,但似乎找不到解决这个问题的办法。我似乎无法在Scala中对其进行正确建模 假设我有一个traitMyTrait,一些不可变的类实现了它 它看起来像这样: trait MyTrait { type Repr <: MyTrait def substitute(original: Item, replacement: Item) : Repr def substituteAll( originals: List[Item], rep
MyTrait
,一些不可变的类实现了它
它看起来像这样:
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(
originals: List[Item],
replacement: Item
) : Repr = {
originals match {
case head :: tail => substitute(head).substituteAll(tail, replacement)
case Nil => this //this complains that this is not of type Repr
}
}
}
trait MyTrait2 { ... }
case class MyClassA(originals: List[Item])
extends MyTrait with MyTrait2 {
type Repr = MyClassA
def substitute(original: Item, replacement: Item) : MyClassA = {
//whatever code that updates the list etc.
MyClassA(newOriginals)
}
}
case class MyClassB(originals: List[Item])
extends MyTrait with MyTrait2 {
type Repr = MyClassB
def substitute(original: Item, replacement: Item) : MyClassB = {
//whatever code that updates the list etc.
MyClassB(newOriginals)
}
}
case class CompoundClass(list : List[MyTrait with MyTrait2])
extends MyTrait {
type Repr = CompoundClass
def substitute(
original: Item,
replacement: Item
) : CompoundClass =
CompoundClass(list.map(
myClass => myClass.substitute(original, replacement)
))
)
//the above complains that it is expecting
// List[MyTrait with MyTrait2]
//while in fact it is getting MyTrait2#Repr
}
trait-MyTrait{
类型Repr替代品(头部)。替代品全部(尾部,更换)
case Nil=>this//this抱怨这不是Repr类型
}
}
}
性状MyTrait2{…}
案例类别MyClassA(原件:清单[项目])
使用MyTrait2扩展MyTrait{
类型Repr=MyClassA
def替代品(原件:项目,替换品:项目):MyClassA={
//任何更新列表的代码等。
MyClassA(新原件)
}
}
案例类别MyClassB(原件:清单[项目])
使用MyTrait2扩展MyTrait{
类型Repr=MyClassB
def替代品(原件:项目,替换品:项目):MyClassB={
//任何更新列表的代码等。
MyClassB(新原件)
}
}
案例类CompoundClass(列表:list[MyTrait with MyTrait2])
扩展我的特质{
类型Repr=CompoundClass
def替代品(
原件:第,
替换:项目
):CompoundClass=
复合类(list.map)(
myClass=>myClass.substitute(原件,替换件)
))
)
//上述投诉表明,它正在期待
//列表[带MyTrait2的MyTrait]
//而事实上,它正在得到我的报告
}
如果我将我的问题总结如下:
- 通过super-trait方法更新不可变类应该返回实现类的相同类型
- Super-trait需要能够在有意义的时候返回
,而不是返回另一个对象。我似乎对此有问题这个
- 我需要能够在不知道实际类型的情况下将超级特性传递给函数。标准多态性。这样函数就可以调用trait的方法,而无需 了解实际的具体类型
- 我需要能够使用组合将类组合到其他类中。(想象一个由子表达式组成的表达式)。这看起来像是标准的构图,但有了上面的内容,我就得到了
#Repr
MyTrait[T]
中使用泛型类型,但这使得我无法传递任何我想要的具体类。我现在正在尝试使用抽象类型,而我在编译时似乎也面临同样的问题。从本质上说,我认为现在真的没有什么不同,我又陷入了同样的陷阱
我做错了什么?我看得不对吗?这里有一些问题,其中一些问题与多态性完全无关 从
substituteAll
方法开始:
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(
originals: List[Item],
replacements: List[Item]
) : Repr = {
originals match {
case head :: tail => substitute(head).substituteAll(tail)
case Nil => this
}
}
}
您的下一个问题是,Repr
type参数不包含您希望它包含的复合类型MyTrait with MyTrait2
这不是一个完整的类型,因为Repr
param仍然是抽象的。您真正想要的是完全指定的类型MyTrait with MyTrait2{type Repr=MyTrait with MyTrait2}
考虑到这有点麻烦,更容易引入另一个特征来表示它:
trait CompoundElem extends MyTrait with MyTrait2 {
type Repr <: CompoundElem
}
如果您想让元素类型保持更长时间,还可以编写最后一个类:
object MyTrait {
//type alias helper to view the type member as though it were a param
//A neat trick, shamelessly borrowed from the shapeless library
type Aux[R] = MyTrait { type Repr = R }
}
case class CompoundClass[E <: MyTrait.Aux[E]](list : List[E]) extends MyTrait {
type Repr = CompoundClass[E]
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map( _.substitute(original, replacement) )
)
}
它将只是一个MyTraits
的CompoundClass
,当您将元素作为ClassA
或ClassB
或MyTrait2
处理时,需要使用模式匹配,但您不需要任何中间特征
最后。。。作为参考,这里有相同的想法,使用F-边界重新实现。注意类型参数如何允许
自类型
,因此不需要在所有子类中明确定义Repr
:
trait Item {}
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(originals: List[Item], replacements: List[Item]) : Repr
}
object MyTrait {
trait Aux[T <: MyTrait.Aux[T]] extends MyTrait { self: T =>
type Repr = T
def substituteAll(originals: List[Item], replacements: List[Item]) : T = {
def loop(pairs: List[(Item, Item)]): Repr = pairs match {
case (orig, rep) :: tail =>
substitute(orig, rep)
loop(tail)
case Nil => this
}
loop(originals zip replacements)
}
}
}
trait MyTrait2 { }
case class MyClassA(originals: List[Item]) extends MyTrait.Aux[MyClassA] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassA(originals)
}
case class MyClassB(originals: List[Item]) extends MyTrait.Aux[MyClassB] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassB(originals)
}
case class CompoundClass(list : List[MyTrait]) extends MyTrait.Aux[CompoundClass] {
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map( _.substitute(original, replacement) )
)
}
trait项{}
性状我的性状{
类型报告
替换(原,代表)
环(尾)
案例Nil=>此
}
循环(原拉链替换件)
}
}
}
性状MyTrait2{}
case类MyClassA(originals:List[Item])用MyTrait2扩展了MyTrait.Aux[MyClassA]{
def替代品(原件:项目,替换品:项目)=MyClassA(原件)
}
案例类MyClassB(originals:List[Item])用MyTrait2扩展了MyTrait.Aux[MyClassB]{
def替代品(原件:项目,替换品:项目)=MyClassB(原件)
}
case类CompoundClass(list:list[MyTrait])扩展了MyTrait.Aux[CompoundClass]{
def替代品(
原件:第,
替换:项目
)=复合类(
列表地图(替换件(原件、替换件))
)
}
乍一看,我怀疑你想要的有些东西是不可能的。但是,在Scala中查找“F-Bounded Polymophism”(F-Bounded Polymophism)的模式,它将允许您实现您的第一个请求:“通过super-trait的方法更新不可变类应该返回相同类型的实现类。”@KevinWright是的,这是对我前面问题的改进。我的场景比前一个场景更详细,但前一个场景不起作用,我仍然有一些困难。@RandallSchulz我的第一个解决方案是使用F-有界多态性,这正是我对MyTrait[T]
的意图。然而,我面临的问题是,无论我在哪里期待MyTrait
作为参数,编译器开始期待[T]
的某种类型,结果我陷入了一片混乱。KevinWright(在上一个问题中)建议我使用一个抽象类型成员,它在小示例中似乎工作得很好,但是实际上我甚至不能将这个返回给从超类返回这个类型成员的函数,并将实例作为参数传递,开始获得#Repr
,正如Randall所说,对于这些用例,您需要使用类型参数的完全F-bound。你仍然可以隐藏它
object MyTrait {
//type alias helper to view the type member as though it were a param
//A neat trick, shamelessly borrowed from the shapeless library
type Aux[R] = MyTrait { type Repr = R }
}
case class CompoundClass[E <: MyTrait.Aux[E]](list : List[E]) extends MyTrait {
type Repr = CompoundClass[E]
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map( _.substitute(original, replacement) )
)
}
case class CompoundClass(list : List[MyTrait]) extends MyTrait {
type Repr = CompoundClass
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map( _.substitute(original, replacement) )
)
}
trait Item {}
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(originals: List[Item], replacements: List[Item]) : Repr
}
object MyTrait {
trait Aux[T <: MyTrait.Aux[T]] extends MyTrait { self: T =>
type Repr = T
def substituteAll(originals: List[Item], replacements: List[Item]) : T = {
def loop(pairs: List[(Item, Item)]): Repr = pairs match {
case (orig, rep) :: tail =>
substitute(orig, rep)
loop(tail)
case Nil => this
}
loop(originals zip replacements)
}
}
}
trait MyTrait2 { }
case class MyClassA(originals: List[Item]) extends MyTrait.Aux[MyClassA] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassA(originals)
}
case class MyClassB(originals: List[Item]) extends MyTrait.Aux[MyClassB] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassB(originals)
}
case class CompoundClass(list : List[MyTrait]) extends MyTrait.Aux[CompoundClass] {
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map( _.substitute(original, replacement) )
)
}