如何使用单片机修改嵌套地图和scala

时间:2016-12-31 18:37:01

标签: scala lenses monocle-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
)

这是我用monocle实现的

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. 我们可以用宏生成所有样板吗?
  3. [更新]我提出了一个组合器

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

    问题在于我仍然需要制作一个中间无用的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 个答案:

答案 0 :(得分:3)

您可以使用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)