scala中的case class是一種特殊的對象:由編譯器(compiler)自動生成欄位的getter和setter。如下麵的例子: 1 case class City(name:String, province: String) 2 case class Address(street: Str
scala中的case class是一種特殊的對象:由編譯器(compiler)自動生成欄位的getter和setter。如下麵的例子:
1 case class City(name:String, province: String)
2 case class Address(street: String, zip: String, city: City)
3 case class Person(name: String, age: Int, phone: String, address: Address)
4 val Peter = Person("Peter Chan",20,"4806111",Address("13 baoan road","40001",City("ShenZhen","GuangDong")))
5 //> Peter : Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13
6 //| baoan road,40001,City(ShenZhen,GuangDong)))
7 val John = Person("John Woo",43,"3602011",Address("33 fada road","51001",City("GuangZhou","GuangDong")))
8 //> John : Exercises.LensDemo.Person = Person(John Woo,43,3602011,Address(33 fa
9 //| da road,51001,City(GuangZhou,GuangDong)))
10 val Ming = Person("Fang Ming",23,"3682412",Address("6 jiefang da dao","51012",City("GuangZhou","GuangDong")))
11 //> Ming : Exercises.LensDemo.Person = Person(Fang Ming,23,3682412,Address(6 ji
12 //| efang da dao,51012,City(GuangZhou,GuangDong)))
這裡我們可以看到:Person是個多層次對象,包含多層嵌入屬性對象(multi-layer embeded objects)。如果需要更改Person類型實例中的任何欄位時,我們可以直接用行令方式(imperative style):
1 case class City(var name:String, province: String)
2 Peter.address.city.name = "DongGuan"
3 Peter //> res0: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba
4 //| oan road,40001,City(DongGuan,GuangDong)))
註意:我必須把case class 屬性City的name欄位屬性變成var,而且這時peter已經轉變了(mutated)。既然我們是在函數式編程中,強調的是純函數代碼,即使用不可變對象(immutable objects),那麼函數式編程方式的欄位操作又可以怎樣呢?
1 al peterDG = peter.copy(
2 address = peter.address.copy(
3 city = peter.address.city.copy(name = "DongGuan")))
4 //> peterDG : Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(
5 //| 13 baoan road,40001,City(DongGuan,GuangDong)))
6 peter //> res0: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba
7 //| oan road,40001,City(ShenZhen,GuangDong)))
8 peterDG //> res1: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 ba
9 //| oan road,40001,City(DongGuan,GuangDong)))
我們可以使用case class的自帶函數copy來實現欄位操作。但是隨著嵌入對象層次的增加,將會產生大量的重覆代碼。scalaz的Lens type class的主要功能之一就可以解決以上問題。我們先來看看scalaz Lens的用例:
1 //定義Lens實例
2 val nameL = Lens.lensu[Person, String]((p,n) => p.copy(name=n), _.name)
3 //> nameL : scalaz.Lens[Exercises.LensDemo.Person,String] = scalaz.LensFunction
4 //| s$$anon$5@5f375618
5 val ageL = Lens.lensu[Person, Int]((p,a) => p.copy(age=a), _.age)
6 //> ageL : scalaz.Lens[Exercises.LensDemo.Person,Int] = scalaz.LensFunctions$$a
7 //| non$5@1810399e
8 val addrL = Lens.lensu[Person,Address]((p,a) => p.copy(address=a), _.address)
9 //> addrL : scalaz.Lens[Exercises.LensDemo.Person,Exercises.LensDemo.Address]
10 //| = scalaz.LensFunctions$$anon$5@32d992b2
11 val zipL = Lens.lensu[Address,String]((a,z) => a.copy(zip=z), _.zip)
12 //> zipL : scalaz.Lens[Exercises.LensDemo.Address,String] = scalaz.LensFunctio
13 //| ns$$anon$5@215be6bb
14 val cityL = Lens.lensu[Address,City]((a,c) => a.copy(city=c), _.city)
15 //> cityL : scalaz.Lens[Exercises.LensDemo.Address,Exercises.LensDemo.City] =
16 //| scalaz.LensFunctions$$anon$5@4439f31e
17 val provL = Lens.lensu[City,String]((c,p) => c.copy(province=p), _.province)
18 //> provL : scalaz.Lens[Exercises.LensDemo.City,String] = scalaz.LensFunctions
19 //| $$anon$5@5dfcfece
20 //具體用例
21 val peter30 = ageL set (peter,30) //> peter30 : Exercises.LensDemo.Person = Person(Peter Chan,30,4806111,Address
22 //| (13 baoan road,40001,City(ShenZhen,GuangDong)))
23 val provCap = provL mod (_.toUpperCase,peter.address.city)
24 //> provCap : Exercises.LensDemo.City = City(ShenZhen,GUANGDONG)
25 provL get provCap //> res2: String = GUANGDONG
26 val newCity = cityL set (peter.address, City("ChengDu","SiChuan"))
27 //> newCity : Exercises.LensDemo.Address = Address(13 baoan road,40001,City(Ch
28 //| engDu,SiChuan))
29 cityL get newCity //> res3: Exercises.LensDemo.City = City(ChengDu,SiChuan)
可以看到:我們用Lens.lensu構建了上面那些case class的Lens實例,然後分別用get,set,mod對case class的欄位進行了讀寫操作示範。理論上Lens的基本定義大約是這樣的:
case class Lens[R,F](
get: R => F,
set: (R,F) => R
)
實際上Lens就是get,set函數的外包(wrapper)。R,F可以分別被理解為記錄Record和欄位Field類型。get和set是lambda表達式,分別代表:給一個Record,返回Field結果;給一個Record及一個欄位值,更新Record中這個欄位值後返回新的Record。
我們看看在scalaz中是如何定義Lens的:scalaz/package.scala
//
// Lens type aliases
//
/** A lens that doesn't transform the type of the record. */
type Lens[A, B] = LensFamily[A, A, B, B]
// important to define here, rather than at the top-level, to avoid Scala 2.9.2 bug
object Lens extends LensInstances with LensFunctions {
def apply[A, B](r: A => Store[B, A]): Lens[A, B] =
lens(r)
}
type @>[A, B] = Lens[A, B]
//
// Partial Lens type aliases
//
/** A partial lens that doesn't transform the type of the record. */
type PLens[A, B] = PLensFamily[A, A, B, B]
// important to define here, rather than at the top-level, to avoid Scala 2.9.2 bug
object PLens extends PLensInstances with PLensFunctions {
def apply[A, B](r: A => Option[Store[B, A]]): PLens[A, B] =
plens(r)
}
type @?>[A, B] = PLens[A, B]
Plens是部分作用(partial)Lens,與Lens不同的是Plens的get和set返回的結果都是Option類型的。我們再看看LensFamily的定義:scalaz/Lens.scala
sealed abstract class LensFamily[A1, A2, B1, B2] {
def run(a: A1): IndexedStore[B1, B2, A2]
def apply(a: A1): IndexedStore[B1, B2, A2] =
run(a)
...
如上可以這樣理解LensFamily的類型參數LensFamily[R1,R2,F1,F2],分別代表操作前後Record和Field的類型。這樣又提供了類型轉換(type transformation)操作,可以概括更多類型的Lens。Lens包嵌了個Store類型,我們看看Store的定義:scalaz/StoreT.scala
final case class IndexedStoreT[F[_], +I, A, B](run: (F[A => B], I)) {
...
及scalaz/package.scala
type StoreT[F[_], A, B] = IndexedStoreT[F, A, A, B]
type IndexedStore[I, A, B] = IndexedStoreT[Id, I, A, B]
type Store[A, B] = StoreT[Id, A, B]
// flipped
type |-->[A, B] = Store[B, A]
object StoreT extends StoreTInstances with StoreTFunctions {
def apply[F[_], A, B](r: (F[A => B], A)): StoreT[F, A, B] =
storeT(r)
}
object IndexedStore {
def apply[I, A, B](f: A => B, i: I): IndexedStore[I, A, B] = IndexedStoreT.indexedStore(i)(f)
}
object Store {
def apply[A, B](f: A => B, a: A): Store[A, B] = StoreT.store(a)(f)
}
首先,Store就是個函數存儲器。與Lens共同使用時它可以存放get和set函數。從Store的存取函數可以分析得見:scalaz/StoreT.scala
...
def put(a: A)(implicit F: Functor[F]): F[B] =
F.map(run._1)(_(a))
def puts(f: I => A)(implicit F: Functor[F]): F[B] =
put(f(pos))
def set: F[A => B] =
run._1
def pos: I =
run._2
...
我們看見:如果F = Id的話,put(a) = run._1(a) >>> run._1 是 A => B,函數輸入A,返回B,是個setter。pos = run._2 = A,是個getter。但Lens的get,set好像是這樣的: get: A => B, set: A => B => A,與Store的A,B剛好相反,不過看看scalaz Lens的構建函數就明白了:scalaz/Lens.scala
trait LensFunctions extends LensFamilyFunctions {
def lens[A, B](r: A => Store[B, A]): Lens[A, B] = new Lens[A, B] {
def run(a: A): Store[B, A] = r(a)
}
def lensg[A, B](set: A => B => A, get: A => B): Lens[A, B] =
lens(a => Store(set(a), get(a)))
def lensu[A, B](set: (A, B) => A, get: A => B): Lens[A, B] =
lensg(set.curried, get)
...
run(A) >>> Store[B,A], 剛好相反。可以說Store的B就是Record類型,A就是Field類型。所以scalaz版本的Lens包嵌的是個Store[F,R]。我們可以用pos和put把獲取get和set。看看LensFamily的get和set源代碼:
sealed abstract class LensFamily[A1, A2, B1, B2] {
...
def get(a: A1): B1 =
run(a).pos
def set(a: A1, b: B2): A2 =
run(a).put(b)
...
實際上即使用Lens過程中為每個欄位定義Lens也會涉及到大量的重覆代碼。但Lens是可組合的(composible),這樣我們可以重覆利用最基本細小的Lens來組合成更大範圍的Lens。scalaz提供了Lens組合函數:
/** Lenses can be composed */
def compose[C1, C2](that: LensFamily[C1, C2, A1, A2]): LensFamily[C1, C2, B1, B2] =
lensFamily(c => {
val (ac, a) = that.run(c).run
val (ba, b) = run(a).run
IndexedStore(ac compose ba, b)
})
/** alias for `compose` */
def <=<[C1, C2](that: LensFamily[C1, C2, A1, A2]): LensFamily[C1, C2, B1, B2] = compose(that)
def andThen[C1, C2](that: LensFamily[B1, B2, C1, C2]): LensFamily[A1, A2, C1, C2] =
that compose this
/** alias for `andThen` */
def >=>[C1, C2](that: LensFamily[B1, B2, C1, C2]): LensFamily[A1, A2, C1, C2] = andThen(that)
我們試著把Lens組合起來做個示範:
1 addrL >=> cityL >=> provL get peter //> res4: String = GuangDong
2
3 val peterFromShanXi = addrL >=> cityL >=> provL set(peter,"ShanXi")
4 //> peterFromShanXi : Exercises.LensDemo.Person = Person(Peter Chan,20,4806111
5 //| ,Address(13 baoan road,40001,City(ShenZhen,ShanXi)))
6 peterFromShanXi //> res5: Exercises.LensDemo.Person = Person(Peter Chan,20,4806111,Address(13 b
7 //| aoan road,40001,City(ShenZhen,ShanXi)))
Lens可以被轉換成State:
def st: State[A1, B1] = State(s => (s, get(s))) abstract class LensInstances extends LensInstances0 { import LensFamily._ import BijectionT._ import collection.SeqLike import collection.immutable.Stack import collection.immutable.Queue implicit val lensCategory: LensCategory = new LensCategory { } /** Lenses may be used implicitly as State monadic actions that get the viewed portion of the state */ implicit def LensFamilyState[A, B](lens: LensFamily[A, _, B, _]): State[A, B] = lens.st ...
這裡有一個隱式類型轉換函數可以把LensFamily轉成State。
scalaz提供了許多函數讓我們可以把Lens操作結果轉化成State類型:
/** Modify the portion of the state viewed through the lens and return its new value. */
def mods(f: B1 => B2): IndexedState[A1, A2, B2] =
IndexedState(a => {
val c = run(a)
val b = f(c.pos)
(c put b, b)
})
/** Modify the portion of the state viewed through the lens and return its new value. */
def %=(f: B1 => B2): IndexedState[A1, A2, B2] =
mods(f)
/** Modify the portion of the state viewed through the lens and return its old value.
* @since 7.0.2
*/
def modo(f: B1 => B2): IndexedState[A1, A2, B1] =
IndexedState(a => {
val c = run(a)
val o = c.pos
(c put f(o), o)
})
/** Modify the portion of the state viewed through the lens and return its old value. alias for `modo`
* @since 7.0.2
*/
def <%=(f: B1 => B2): IndexedState[A1, A2, B1] =
modo(f)
/** Set the portion of the state viewed through the lens and return its new value. */
def assign(b: => B2): IndexedState[A1, A2, B2] =
mods(_ => b)
/** Set the portion of the state viewed through the lens and return its new value. */
def :=(b: => B2): IndexedState[A1, A2, B2] =
assign(b)
/** Set the portion of the state viewed through the lens and return its old value.
* @since 7.0.2
*/
def assigno(b: => B2): IndexedState[A1, A2, B1] =
modo(_ => b)
/** Set the portion of the state viewed through the lens and return its old value. alias for `assigno`
* @since 7.0.2
*/
def <:=(b: => B2): IndexedState[A1, A2, B1] =
assigno(b)
/** Modify the portion of the state viewed through the lens, but do not return its new value. */
def mods_(f: B1 => B2): IndexedState[A1, A2, Unit] =
IndexedState(a =>
(mod(f, a), ()))
/** Modify the portion of the state viewed through the lens, but do not return its new value. */
def %==(f: B1 => B2): IndexedState[A1, A2, Unit] =
mods_(f)
/** Contravariantly map a state action through a lens. */
def lifts[C](s: IndexedState[B1, B2, C]): IndexedState[A1, A2, C] =
IndexedState(a => modp(s(_), a))
def %%=[C](s: IndexedState[B1, B2, C]): IndexedState[A1, A2, C] =
lifts(s)
/** Map the function `f` over the lens as a state action. */
def map[C](f: B1 => C): State[A1, C] =
State(a => (a, f(get(a))))
/** Map the function `f` over the value under the lens, as a state action. */
def >-[C](f: B1 => C): State[A1, C] = map(f)
/** Bind the function `f` over the value under the lens, as a state action. */
def flatMap[C](f: B1 => IndexedState[A1, A2, C]): IndexedState[A1, A2, C] =
IndexedState(a => f(get(a))(a))
/** Bind the function `f` over the value under the lens, as a state action. */
def >>-[C](f: B1 => IndexedState[A1, A2, C]): IndexedState[A1, A2, C] =
flatMap[C](f)
/** Sequence the monadic action of looking through the lens to occur before the state action `f`. */
def ->>-[C](f: => IndexedState[A1, A2, C]): IndexedState[A1, A2, C] =
flatMap(_ => f)
因為State是個Monad,我們可以用for-comprehension來實現行令式編程:
1 nameL run peter //> res6: scalaz.IndexedStore[String,String,Exercises.LensDemo.Person] = Indexe
2 //| dStoreT((<function1>,Peter Chan))
3 cityL run peter.address //> res7: scalaz.IndexedStore[Exercises.LensDemo.City,Exercises.LensDemo.City,E
4 //| xercises.LensDemo.Address] = IndexedStoreT((<function1>,City(ShenZhen,GuangDong)))
5 val updateInfo = for {
6 _ <- ageL += 1
7 zip <- addrL >=> zipL
8 _ <- (addrL >=> zipL) := zip + "A"
9 _ <- nameL %= {n => n + " Junior"}
10 _ <- (addrL >=> cityL) := City("GuangZhou","GuangDong")
11 info <- get
12 } yield info //> updateInfo : scalaz.IndexedStateT[scalaz.Id.Id,Exercises.LensDemo.Person,E
13 //| xercises.LensDemo.Person,Exercises.LensDemo.Person] = scalaz.IndexedStateT$
14 //| $anon$10@74a10858
15 updateInfo eval peter //> res8: scalaz.Id.Id[Exercises.LensDemo.Person] = Person(Peter Chan Junior,21
16 //| ,4806111,Address(13 baoan road,40001A,City(GuangZhou,GuangDong)))
這是一段比較典型的行令程式。
上面例子的 += 是NumericLens一項操作。NumericLens是這樣定義的:
type NumericLens[S, N] = NumericLensFamily[S, S, N] val NumericLens: NumericLensFamily.type = NumericLensFamily /** Allow the illusion of imperative updates to numbers viewed through a lens */ case class NumericLensFamily[S1, S2, N](lens: LensFamily[S1, S2, N, N], num: Numeric[N]) { def +=(that: N): IndexedState[S1, S2, N] = lens %= (num.plus(_, that)) def -=(that: N): IndexedState[S1, S2, N] = lens %= (num.minus(_, that)) def *=(that: N): IndexedState[S1, S2, N] = lens %= (num.times(_, that)) } implicit def numericLensFamily[S1, S2, N: Numeric](lens: LensFamily[S1, S2, N, N]) = NumericLens[S1, S2, N](lens, implicitly[Numeric[N]])
scalar還提供了許多標準數據類型的Lens:
trait LensFunctions extends LensFamilyFunctions { def lens[A, B](r: A => Store[B, A]): Lens[A, B] = new Lens[A, B] { def run(a: A): Store[B, A] = r(a) } def lensg[A, B](set: A => B => A, get: A => B): Lens[A, B] = lens(a => Store(set(a), get(a))) def lensu[A, B](set: (A, B) => A, get: A => B): Lens[A, B] = lensg(set.curried, get) /** The identity lens for a given object */ def lensId[A]: Lens[A, A] = lens(Store(identity, _)) /** The trivial lens that can retrieve Unit from anything */ def trivialLens[A]: Lens[A, Unit] = lens[A, Unit](a => Store(_ => a, ())) /** A lens that discards the choice of right or left from disjunction */ def codiagLens[A]: Lens[A \/ A, A] = lensId[A] ||| lensId[A] /** Access the first field of a tuple */ def firstLens[A, B]: (A, B) @> A = lens { case (a, b) => Store(x => (x, b), a) } /** Access the second field of a tuple */ def secondLens[A, B]: (A, B) @> B = lens { case (a, b) => Store(x => (a, x), b) } /** Access the first field of a tuple */ def lazyFirstLens[A, B]: LazyTuple2[A, B] @> A = lens(z => Store(x => LazyTuple2(x, z._2), z._1)) /** Access the second field of a tuple */ def lazySecondLens[A, B]: LazyTuple2[A, B] @> B = lens(z => Store(x => LazyTuple2(z._1, x), z._2)) def nelHeadLens[A]: NonEmptyList[A] @> A = lens(l => Store(NonEmptyList.nel(_, l.tail), l.head)) def nelTailLens[A]: NonEmptyList[A] @> List[A] = lens(l => Store(NonEmptyList.nel(l.head, _), l.tail)) /** Access the value at a particular key of a Map **/ def mapVLens[K, V](k: K): Map[K, V] @> Option[V] = lensg(m => ({ case None => m - k case Some(v) => m.updated(k, v) }: Option[V] => Map[K, V]), _ get k) /** Access the value at a particular key of a Map.WithDefault */ def mapWithDefaultLens[K,V](k: K): Map.WithDefault[K,V] @> V = lensg(m => v => m.updated(k,v), m => m(k)) /** Specify whether a value is in a Set */ def setMembershipLens[A](a: A): Set[A] @> Boolean = lensg(s => b => if (b) s + a else s - a, _.contains(a)) def applyLens[A, B](k: B => A)(implicit e: Equal[A]): Store[A, B] @> B = lens(q => { lazy val x = q.pos lazy val y = q put x Store(b => Store(w => if(e equal (x, w)) b else y, x), y) }) def predicateLens[A]: Store[A, Boolean] @> (A \/ A) = lens(q => Store(_ match { case -\/(l) => Store(_ => true, l) case \/-(r) => Store(_ => false, r) }, { val x = q.pos if(q put x) -\/(x) else \/-(x) })) def factorLens[A, B, C]: ((A, B) \/ (A, C)) @> (A, B \/ C) = lens(e => Store({ case (a, -\/(b)) => -\/(a, b) case (a, \/-(c)) => \/-(a, c) }, e match { case -\/((a, b)) => (a, -\/(b)) case \/-((a, c)) => (a, \/-(c)) })) def distributeLens[A, B, C]: (A, B \/ C) @> ((A, B) \/ (A, C)) = lens { case (a, e) => Store({ case -\/((aa, bb)) => (aa, -\/(bb)) case \/-((aa, cc)) => (aa, \/-(cc)) }, e match { case -\/(b) => -\/(a, b) case \/-(c) => \/-(a, c) }) } }
我們下麵示範一些用例:
1 import scala.collection.immutable.Set
2 val set135 = Set(1,3,5) //> set135 : scala.collection.immutable.Set[Int] = Set(1, 3, 5)
3 Lens.setMembershipLens(3) get set135 //> res0: Boolean = true
4 Lens.setMembershipLens(7).set(set135,true) //> res1: Set[Int] = Set(1, 3, 5, 7)
5 Lens.setMembershipLens(5).set(set135,false) //> res2: Set[Int] = Set(1, 3)
6
7
8 import scala.collection.immutable.Map
9 val fooMap = Map(1 -> "A", 2 -> "B") //> fooMap : scala.collection.immutable.Map[Int,String] = Map(1 -> A, 2 -> B)
10 Lens.mapVLens(1) get fooMap //> res3: Option[String] = Some(A)
11 Lens.mapVLens(3).set(fooMap,"C".some) //> res4: Map[Int,String] = Map(1 -> A, 2 -> B, 3 -> C)
12 Lens.mapVLens(1).set(fooMap,None) //> res5: Map[Int,String] = Map(2 -> B)
以上是針對獨立的immutable set和immutable map的操作。與上面的NumericLens示範一樣,scalaz還提供了針對包嵌在對象內屬性的標準類型操作函數,比如如果上面例子的set和map是case class的欄位時該如何操作:
1 case class Company(name: String, employees: Set[String], ids: Map[Int,String], breaks: Queue[String])
2 val titleL = Lens.lensu[Company,String]((c,n) => c.copy(name = n), _.name)
3 //> titleL : scalaz.Lens[Exercises.LensDemo.Company,String] = scalaz.LensFunct
4 //| ions$$anon$5@385c9627
5 val empSetLens = Lens.lensu[Company,Set[String]]((c,e) => c.copy(employees = e), _.employees)
6 //> empSetLens : scalaz.Lens[Exercises.LensDemo.Company,scala.collection.immut
7 //| able.Set[String]] = scalaz.LensFunctions$$anon$5@139982de
8 val idMapLens = Lens.lensu[Company,Map[Int,String]]((c,i) => c.copy(ids = i), _.ids)
9 //> idMapLens : scalaz.Lens[Exercises.LensDemo.Company,scala.collection.immuta
10 //| ble.Map[Int,String]] = scalaz.LensFunctions$$anon$5@682b2fa
11 val breakQLens = Lens.lensu[Company,Queue[String]]((c,b) => c.copy(breaks = b), _.breaks)
12 //> breakQLens : scalaz.Lens[Exercises.LensDemo.Company,scala.collection.immut
13 //| able.Queue[String]] = scalaz.LensFunctions$$anon$5@217ed35e
14
15 val foos = Company("The Foos Co, Ltd.",
16 Set("peter","john","susan"),
17 Map(1 -> "peter", 2 -> "john", 3 -> "susan"),
18 Queue("breakfast","lunch","dinner")) //> foos : Exercises.LensDemo.Company = Company(The Foos Co, Ltd.,Set(peter, j
19 //| ohn, susan),Map(1 -> peter, 2 -> john, 3 -> susan),Queue(breakfast, lunch,
20 //| dinner))
21 val newCompany = for {
22 n <- titleL
23 _ <- titleL := "The New Foo Foo Company"
24 _ <- empSetLens -= "john"
25 _ <- empSetLens += "kitty"
26 _ <- idMapLens -= 2
27 _ <- idMapLens += (2 -> "kitty")
28 bf <- breakQLens.dequeue
29 lc <- breakQLens.dequeue
30 dn <- breakQLens.dequeue
31 _ <- breakQLens.enqueue(bf)
32 _ <- breakQLens.enqueue(lc)
33 _ <- breakQLens.enqueue("afternoon tea")
34 _ <- breakQLens.enqueue(dn)
35 newOne <- get
36 } yield newOne //> newCompany : scalaz.IndexedState[Exercises.LensDemo.Company,Exercises.Lens
37 //| Demo.Company,Exercises.LensDemo.Company] = scalaz.package$IndexedState$$ano
38 //| n$2@2cd76f31
39 newCompany eval foos //> res7: scalaz.Id.Id[Exercises.LensDemo.Company] = Company(The New Foo Foo Co
40 //| mpany,Set(peter, susan, kitty),Map(1 -> peter, 3 -> susan, 2 -> kitty),Queu
41 //| e(breakfast, lunch, afternoon tea, dinner))
當然,scalaz提供的還有其它類型的Lens,這裡就不一一示範了,具體可以參考源代碼scalaz/Lens.scala
從以上討論我們瞭解到Lens不但解決了多層嵌入屬性操作重覆代碼問題,它還可以進行函數組合,實現重覆使用基本Lens組合獲取各種不同功能的Lens。最重要的是Lens與State一同使用可以讓我們採用行令編程方式來對對象的嵌入屬性進行操作。