Scala 不可变类的多态更新

Scala 不可变类的多态更新,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

我一直在想方设法解决这个问题,但似乎找不到解决这个问题的办法。我似乎无法在Scala中对其进行正确建模

假设我有一个trait
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) )
  )
}