Haskell的Lens是一個怎樣的庫?
1. 它解決了什麼問題?
2. 它提出了哪些基本概念?希望大大們可以舉一些比較具體的例子說明一下,謝啦!
一個Lens不過就是 Coalgebra for the CoState Comonad,這有什麼難以理解的╮(╯▽╰)╭ 。(逃
Java裡要改掉層層資料結構裡的值, 只需要把參照變數串起來, 代碼複雜度O(n)
a.b.c.d.e = 10但Haskell每多一層, 每個等號就會重複之前等號的序列並多一個取值用的函數, 代碼複雜度O(n^2)a &<- a{b=(b a){c=(c (b a)){d=(d (c (b a))){e=10}}}}有了Lens之後, 尋參照就變成可組合的, 代碼複雜度降級O(n)
a &<- over (b . c . d . e) (\_ -&> 10) a可組合的尋參照a,b,c,d等, 可以用Template Haskell與makeLenses自動產生, 也可以手動, 但這裡就不節外生枝這庫我很少用到, 其他方面就不清楚了對不可變數據的update操作, 若是Scala則鬼畜多了:
// imperative
a.b.c.d.e += 1
// functional
a.copy(
b = a.b.copy(
c = a.b.c.copy(
d = a.b.c.d.copy(
e = a.b.c.d.e + 1
))))
Lens就是用來updates to immutable data的,是一種Functional Design Pattern
在一般情況下不常用,但是數據結構複雜並且是不可變的情況下還是很有用的,故介紹一下其主要就是兩個介面:get: A =&> Bset: (A, B) =&> A
如下不可變數據結構case class A(b: B)
case class B(c: C)
case class C(d: D)
case class D(e: Int)
val a = A(B(C(D(e = 1))))
val aa = a.copy( //鬼畜copy模式
b = a.b.copy(
c = a.b.c.copy(
d = a.b.c.d.copy(
e = a.b.c.d.e + 1
))))
println(aa) //A(B(C(D(2))))
為各個嵌套層級創建Lens(鏡頭),這樣就可以操作每一層數據結構了:
val ALensB = Lens.lensu[A, B] (
(a, value) =&> a.copy(b = value), //get
_.b //set
)
val BLensC = Lens.lensu[B, C] (
(b, value) =&> b.copy(c = value),
_.c
)
val CLensD = Lens.lensu[C, D] (
(c, value) =&> c.copy(d = value),
_.d
)
val DLensE = Lens.lensu[D, Int] (
(d, value) =&> d.copy(e = value),
_.e
)
println(ALensB get a) //B(C(D(1)))
println(ALensB set (a, B(C(D(e = 2))))) //A(B(C(D(2))))
println(BLensC get a.b) //C(D(1))
println(BLensC set (a.b, C(D(e = 2)))) //B(C(D(2)))
println(CLensD get a.b.c) //D(1)
println(CLensD set (a.b.c, D(e = 2))) //C(D(2))
println(DLensE get a.b.c.d) //1
println(DLensE set (a.b.c.d, 2)) //D(2)
Lens 實現了 &>=&> &<=&< , 所以可以將各層串聯起來(多個鏡頭套在一起),直接實現如一開始的鬼畜copy模式:
val ALensE = ALensB &>=&> BLensC &>=&> CLensD &>=&> DLensE
println(ALensE get a) //1
println(ALensE set (a, a.b.c.d.e + 1)) //D(2)
Lens 還可以變為State
//Lens 返回State
val incE = for {
x &<- ALensE %= {_ + 1}
} yield x
//%= 接受一個 B =&> B 返回一個State(Lens為LensT[F[+_], A, B])
val aaa = incE(aa)
println(aaa) //(A(B(C(D(3)))),3)
Lens的中文翻譯是鏡頭,也就是可以聚焦,在一個變數中只關注你關心的部分。比如說,對於一個(,,,,)類型的變數,你可以通過
element2 = (1, 2, 3, 4, 5)^._2
(_, element2, _, _, _) = (1, 2, 3, 4, 5)
set _2 0 (1,2,3,4,5)
就可以把第二個元素設置成0
推薦閱讀:
※如何解釋 Lisp 中 call/cc 的概念?
※如何向一個只有命令式編程背景的人解釋函數式編程中的函數?
※如何看待編程語言越來越大的趨勢?
※怎樣理解 Continuation-passing style?
※如何用Haskell實現面向對象編程?