Как использовать монокль для изменения вложенной карты и другого поля в Scala

Я впервые пробую монокль.

Вот класс случая:

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

И текущая модификация, используя стандартную scala, которую я хотел бы сделать:

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

И вот моя реализация с моноклем

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

К сожалению, код не чище...

  1. Есть ли более лаконичный способ?
  2. Можем ли мы сгенерировать весь шаблон с помощью макросов?

[обновление] Я придумал комбинатор

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

Проблема в том, что мне все равно нужно производить промежуточного и бесполезного С.

[update2] Я убрал код для комбинатора.

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

1 ответ

Решение

Вы можете получить немного более короткую версию, используя index вместо at:

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

Тем не мение, mergeLens также известный как горизонтальная композиция не удовлетворяет Lens закон, если два Lenses указать на то же поле:

  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)
Другие вопросы по тегам