Scala: Sammeln von Updates / Änderungen des unveränderlichen Zustands

Ich versuche derzeit, einen funktionaleren Programmierstil auf ein Projekt anzuwenden, das eine GUI-Entwicklung auf niedriger Ebene (LWJGL-basiert) beinhaltet. Offensichtlich ist es in einem solchen Fall notwendig, viel Staat mit sich herumzutragen, was in der aktuellen Version veränderlich ist. Mein Ziel ist es, irgendwann einen völlig unveränderlichen Zustand zu erreichen, um Zustandsänderungen als Nebeneffekt zu vermeiden. Ich habe eine Weile die Objektive und staatlichen Monaden von scalaz studiert, aber mein Hauptanliegen bleibt: Alle diese Techniken beruhen auf dem Kopieren beim Schreiben. Da mein Bundesstaat sowohl eine große Anzahl von Feldern als auch einige Felder von beträchtlicher Größe hat, mache ich mir Sorgen um die Leistung.

Meines Wissens ist der gebräuchlichste Ansatz zum Ändern unveränderlicher Objekte die Verwendung der generiertencopy Methode von acase class (Dies ist auch, was Objektive unter der Haube tun). Meine erste Frage ist, wie das gehtcopy Methode ist tatsächlich implementiert? Ich habe ein paar Experimente mit einer Klasse wie:

case class State(
  innocentField: Int, 
  largeMap: Map[Int, Int], 
  largeArray: Array[Int]
)

Durch Benchmarking und auch durch Betrachtung des Outputs von-Xprof es sieht aus wie eine AktualisierungsomeState.copy(innocentField = 42) Tatsächlich führt eine tiefe Kopie und ich beobachte einen erheblichen Leistungsabfall, wenn ich die Größe vonlargeMap undlargeArray. Ich hatte irgendwie erwartet, dass die neu konstruierte Instanz die Objektreferenzen des ursprünglichen Zustands teilt, da intern die Referenz nur an den Konstruktor übergeben werden sollte. Kann ich dieses Deep Copy-Verhalten irgendwie erzwingen oder deaktivieren?copy?

Während ich über das Copy-on-Write-Problem nachdachte, fragte ich mich, ob es allgemeinere Lösungen für dieses Problem in FP gibt, in denen Änderungen von unveränderlichen Daten auf eine Art inkrementelle Weise gespeichert werden (im Sinne von "Sammeln von Updates" oder "Sammeln" Änderungen"). Zu meiner Überraschung konnte ich nichts finden und versuchte folgendes:

// example state with just two fields
trait State {
  def getName: String
  def getX: Int

  def setName(updated: String): State = new CachedState(this) {
    override def getName: String = updated
  }
  def setX(updated: Int): State = new CachedState(this) {
    override def getX: Int = updated
  }

  // convenient modifiers
  def modName(f: String => String) = setName(f(getName))
  def modX(f: Int => Int) = setX(f(getX))

  def build(): State = new BasicState(getName, getX)
}

// actual (full) implementation of State
class BasicState(
  val getName: String, 
  val getX: Int
) extends State


// CachedState delegates all getters to another state
class CachedState(oldState: State) extends State {
  def getName = oldState.getName
  def getX    = oldState.getX
}

Nun können Sie so etwas tun:

var s: State = new BasicState("hello", 42)

// updating single fields does not copy
s = s.setName("world")
s = s.setX(0)

// after a certain number of "wrappings"
// we can extract (i.e. copy) a normal instance
val ns = s.setName("ok").setX(40).modX(_ + 2).build()

Meine Frage ist nun: Was haltet ihr von diesem Design? Ist dies eine Art FP-Entwurfsmuster, das ich nicht kenne (abgesehen von der Ähnlichkeit mit dem Builder-Muster)? Da ich nichts Ähnliches gefunden habe, frage ich mich, ob bei diesem Ansatz ein größeres Problem vorliegt. Oder gibt es weitere Standardmethoden, um den Copy-on-Write-Engpass zu beheben, ohne die Unveränderlichkeit aufzugeben?

Gibt es überhaupt eine Möglichkeit, die Funktionen get / set / mod auf irgendeine Weise zu vereinheitlichen?

Bearbeiten:

Meine Vermutung dascopy Eine tiefe Kopie durchzuführen war in der Tat falsch.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage