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 =&> B

set: (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和State可在不可變數據結構之上模擬命令式編程

More: learning Scalaz a€」 Lens


Lens的中文翻譯是鏡頭,也就是可以聚焦,在一個變數中只關注你關心的部分。

比如說,對於一個(,,,,)類型的變數,你可以通過

element2 = (1, 2, 3, 4, 5)^._2

來獲得這個元組的第三個元素,不藉助Lense的話,可能就只有靠模式匹配來做到了:

(_, element2, _, _, _) = (1, 2, 3, 4, 5)

另外,Lens也提供了非常方便的對不可變(immutable)變數更新的功能,比如,要把上面的(1, 2, 3, 4, 5)變成(1,0,3,4,5) 只需要

set _2 0 (1,2,3,4,5)

就可以把第二個元素設置成0


推薦閱讀:

如何解釋 Lisp 中 call/cc 的概念?
如何向一個只有命令式編程背景的人解釋函數式編程中的函數?
如何看待編程語言越來越大的趨勢?
怎樣理解 Continuation-passing style?
如何用Haskell實現面向對象編程?

TAG:函數式編程 | Haskell |