/ / Come usare monocle per modificare una mappa annidata e un altro campo in scala - scala, lens, monocle-scala

Come usare monocle per modificare una mappa nidificata e un altro campo in scala-scala, lenti, monocle-scala

Sto provando con il monocolo per la prima volta.

Ecco la classe del caso:

case class State(mem: Map[String, Int], pointer: Int)

E l'attuale modifica, usando scala standard, che vorrei fare:

def add1 = (s: State) => s.copy(
mem = s.mem.updated("a", s.mem("a") + 1),
pointer = s.pointer + 1
)

Ed ecco la mia implementazione con monocolo

val mem = GenLens[State](_.mem)
val pointer = GenLens[State](_.pointer)
val add2 = (mem composeLens at("a")).modify(_.map(_ + 1)) andThen pointer.modify(_ + 1)

Sfortunatamente, il codice non è più pulito ...

  1. C'è un modo più conciso?
  2. Possiamo generare tutto il boilerplate con le macro?

[aggiornamento] Ho inventato un combinatore

  def combine[S, A, B](lsa : Lens[S, A], f: A => A, lsb: Lens[S, B], g: B => B) : S => S = { s =>
val a = lsa.get(s)
val b = lsb.get(s)
val s2 = lsa.set(f(a))
val s3 = lsb.set(g(b))
s2(s3(s))
}

Il problema è che devo ancora produrre un intermediario e inutile S.

[update2] Ho ripulito il codice per il combinatore.

  def mergeLens[S, A, B](lsa : Lens[S, A], lsb : Lens[S, B]) : Lens[S, (A, B)] =
Lens.apply[S, (A, B)](s => (lsa.get(s), lsb.get(s)))(t => (lsa.set(t._1) andThen lsb.set(t._2)))

def combine[S, A, B](lsa : Lens[S, A], f: A => A, lsb: Lens[S, B], g: B => B) : S => S = {
mergeLens(lsa, lsb).modify { case (a, b) => (f(a), g(b)) }
}

risposte:

2 per risposta № 1

Puoi ottenere una versione leggermente più breve usando index invece di at:

(mem composeOptional index("a")).modify(_ + 1) andThen pointer.modify(_ + 1)

Però, mergeLens noto anche come composizione orizzontale non soddisfa il Lens legge se i due Lenses puntare allo stesso campo:

  import monocle.macros.GenLens

case class Person(name: String, age: Int)
val age = GenLens[Person](_.age)

val age2 = mergeLens(age, age)
val john = Person("John", 25)
age2.get(age2.set((5, 10))(john)) != (5, 10)