Search code examples
scalaclassimmutabilityfolditerable

Copy fields of case class by using iteration over collection


I have few functions that change only one collection in case class per iter via copy method. Everything, works well when i use something like this this.addA.addB.addC. I thought that i can do the same with foldLeft on case class with copy method, unfortunately, it doesn't work in this case. Could you explain please:

1.Why fold's buffer always link to state and not on buffer from prev. iter?

2.How to implement such method?

case class SimpleState(list: List[String] = Nil) {

  def addA = copy(list = "4" :: list)
  def addB = copy(list = "5" :: list)
  def addC = copy(list = "6" :: list)
  val bufferABC = List("A","B","C")
  // must iterate over collection
  // expect "A","B","C","4","5","6" in result state
  // ordering doesn't matter
  def addABC = ???

  def addABCfold = bufferABC.foldLeft(this){case (state,str) =>
    state.copy(list = str::list)
  }

  def addABCiter = copy(list = bufferABC ::: list)
}
/////////////// output ////////////////////
val state = SimpleState()
val state2 = state.addA.addB.addC

val addABCfold = state.addA.addB.addC.addABCfold

val addABCiter = state.addA.addB.addC.addABCiter

state.addA.addB.addC.addABC

and ofc, output result:

state: SimpleState = SimpleState(List())

state2: SimpleState = SimpleState(List(6, 5, 4))

addABCfold: SimpleState = SimpleState(List(C, 6, 5, 4))

addABCiter: SimpleState = SimpleState(List(A, B, C, 6, 5, 4))

scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(ws.sc2:280) at annette.exon.projects.project.wbs.A$A230$A$A230$SimpleState.addABC(ws.sc2:10) at #worksheet#.#worksheet#(ws.sc2:25)


Solution

  • You repeatedly override the list in the copy by str::list, where list is the constant member from the enclosing class. You have to take list from the previous state:

    case class SimpleState(list: List[String] = Nil) {
    
      def addA = copy(list = "4" :: list)
      def addB = copy(list = "5" :: list)
      def addC = copy(list = "6" :: list)
      val bufferABC = List("A","B","C")
      // must iterate over collection
      // expect "A","B","C","4","5","6" in result state
      // ordering doesn't matter
      def addABC = ???
    
      def addABCfold = bufferABC.reverse.foldLeft(this){
        case (state,str) =>
          state.copy(list = str::state.list)
      }
    
      def addABCiter = copy(list = bufferABC ::: list)
    }
    val state = SimpleState()
    println(state.addA.addB.addC.addABCfold)
    println(state.addA.addB.addC.addABCiter)
    

    This outputs:

    SimpleState(List(A, B, C, 6, 5, 4))
    SimpleState(List(A, B, C, 6, 5, 4))
    

    I don't see any advantages over simple ::: though...