Scala: coletando atualizações / mudanças de estado imutável

Atualmente, estou tentando aplicar um estilo de programação mais funcional a um projeto envolvendo desenvolvimento de GUI de baixo nível (baseado em LWJGL). Obviamente, em tal caso, é necessário transportar um monte de estado, que é mutável na versão atual. Meu objetivo é finalmente ter um estado completamente imutável, a fim de evitar mudanças de estado como efeito colateral. Eu estudei as lentes de scalaz e monads de estado por algum tempo, mas minha principal preocupação permanece: todas essas técnicas dependem de copy-on-write. Como meu estado tem um grande número de campos e também alguns campos de tamanho considerável, estou preocupado com o desempenho.

Para meu conhecimento, a abordagem mais comum para modificar objetos imutáveis ​​é usar o método gerado.copy método de umcase class (isso também é o que as lentes fazem sob o capô). Minha primeira pergunta é como issocopy método é realmente implementado? Eu realizei algumas experiências com uma classe como:

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

Por benchmarking e também olhando para a saída de-Xprof parece que está atualizandosomeState.copy(innocentField = 42) realmente realiza uma cópia profunda e eu observo uma queda significativa de desempenho quando eu aumentar o tamanho delargeMap elargeArray. Eu estava de alguma forma esperando que a instância recém-construída compartilha as referências de objeto do estado original, uma vez que internamente a referência deve ser passada para o construtor. Posso de alguma forma forçar ou desabilitar esse comportamento de cópia profunda do padrãocopy?

Enquanto refletia sobre o problema da cópia na gravação, fiquei me perguntando se há soluções mais gerais para esse problema em FP, que armazenam alterações de dados imutáveis ​​de uma maneira incremental (no sentido de "coletar atualizações" ou "reunir" alterar"). Para minha surpresa, não consegui encontrar nada, então tentei o seguinte:

// 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
}

Agora isso permite fazer algo assim:

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()

Minha pergunta agora é: o que você acha desse design? Isso é algum tipo de padrão de design FP que eu não conheço (além da semelhança com o padrão Builder)? Desde que eu não encontrei nada semelhante, eu estou querendo saber se há algum problema importante com esta abordagem? Ou existem maneiras mais padronizadas de resolver o gargalo da cópia na gravação sem desistir da imutabilidade?

Existe ainda a possibilidade de unificar as funções get / set / mod de alguma forma?

Editar:

Minha suposição de quecopy realiza uma cópia profunda foi de fato errado.

questionAnswers(2)

yourAnswerToTheQuestion