2016-12-31 26 views
2

我正嘗試第一次使用單片眼鏡。如何使用單片眼鏡修改嵌套地圖和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)) 
    } 

的問題是,我仍然需要產生的中介和無用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)) } 
    } 

回答

2

可以使用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) 
相關問題