Darren Hester for openphoto.net

### Lens

``````scala> case class Point(x: Double, y: Double)
defined class Point

scala> case class Color(r: Byte, g: Byte, b: Byte)
defined class Color

scala> case class Turtle(
position: Point,
color: Color)

scala> Turtle(Point(2.0, 3.0), 0.0,
Color(255.toByte, 255.toByte, 255.toByte))
res0: Turtle = Turtle(Point(2.0,3.0),0.0,Color(-1,-1,-1))
``````

ここで不変性を壊さずに亀を前進させたい。

``````scala> case class Turtle(position: Point, heading: Double, color: Color) {
def forward(dist: Double): Turtle =
copy(position =
position.copy(
x = position.x + dist * math.cos(heading),
y = position.y + dist * math.sin(heading)
))
}
defined class Turtle

scala> Turtle(Point(2.0, 3.0), 0.0,
Color(255.toByte, 255.toByte, 255.toByte))
res10: Turtle = Turtle(Point(2.0,3.0),0.0,Color(-1,-1,-1))

scala> res10.forward(10)
res11: Turtle = Turtle(Point(12.0,3.0),0.0,Color(-1,-1,-1))
``````

``````// 命令型
a.b.c.d.e += 1

// 関数型
a.copy(
b = a.b.copy(
c = a.b.c.copy(
d = a.b.c.d.copy(
e = a.b.c.d.e + 1
))))
``````

この余計な `copy` の呼び出しを何とかしたい。

Scalaz 7 の `Lens` をみてみる:

``````  type Lens[A, B] = LensT[Id, A, B]

object Lens extends LensTFunctions with LensTInstances {
def apply[A, B](r: A => Store[B, A]): Lens[A, B] =
lens(r)
}
``````

### LensT

`LensT` はこうなっている:

``````import StoreT._
import Id._

sealed trait LensT[F[+_], A, B] {
def run(a: A): F[Store[B, A]]
def apply(a: A): F[Store[B, A]] = run(a)
...
}

object LensT extends LensTFunctions with LensTInstances {
def apply[F[+_], A, B](r: A => F[Store[B, A]]): LensT[F, A, B] =
lensT(r)
}

trait LensTFunctions {
import StoreT._

def lensT[F[+_], A, B](r: A => F[Store[B, A]]): LensT[F, A, B] = new LensT[F, A, B] {
def run(a: A): F[Store[B, A]] = r(a)
}

def lensgT[F[+_], A, B](set: A => F[B => A], get: A => F[B])(implicit M: Bind[F]): LensT[F, A, B] =
lensT(a => M(set(a), get(a))(Store(_, _)))
def lensg[A, B](set: A => B => A, get: A => B): Lens[A, B] =
lensgT[Id, A, B](set, get)
def lensu[A, B](set: (A, B) => A, get: A => B): Lens[A, B] =
lensg(set.curried, get)
...
}
``````

### Store

`Store` って何だろう?

``````  type Store[A, B] = StoreT[Id, A, B]
// flipped
type |-->[A, B] = Store[B, A]
object Store {
def apply[A, B](f: A => B, a: A): Store[A, B] = StoreT.store(a)(f)
}
``````

とりあえず setter (`A => B => A`) と getter (`A => B`) のラッパーらしい。

### Lens を使う

`turtlePosition``pointX` を定義してみよう:

``````scala> val turtlePosition = Lens.lensu[Turtle, Point] (
(a, value) => a.copy(position = value),
_.position
)
turtlePosition: scalaz.Lens[Turtle,Point] = scalaz.LensTFunctions\$\$anon\$5@421dc8c8

scala> val pointX = Lens.lensu[Point, Double] (
(a, value) => a.copy(x = value),
_.x
)
pointX: scalaz.Lens[Point,Double] = scalaz.LensTFunctions\$\$anon\$5@30d31cf9
``````

``````scala> val turtleX = turtlePosition >=> pointX
turtleX: scalaz.LensT[scalaz.Id.Id,Turtle,Double] = scalaz.LensTFunctions\$\$anon\$5@11b35365
``````

`Turtle` から `Double` に向かっているわけだから、型は理にかなっている。`get` メソッドを使って値を取得できる:

``````scala> val t0 = Turtle(Point(2.0, 3.0), 0.0,
Color(255.toByte, 255.toByte, 255.toByte))
t0: Turtle = Turtle(Point(2.0,3.0),0.0,Color(-1,-1,-1))

scala> turtleX.get(t0)
res16: scalaz.Id.Id[Double] = 2.0
``````

``````scala> turtleX.set(t0, 5.0)
res17: scalaz.Id.Id[Turtle] = Turtle(Point(5.0,3.0),0.0,Color(-1,-1,-1))
``````

これもうまくいった。値を `get` して、なんらかの関数に適用した後、結果を `set` したい場合はどうすればいいだろう? `mod` がそれを行う:

``````scala> turtleX.mod(_ + 1.0, t0)
res19: scalaz.Id.Id[Turtle] = Turtle(Point(3.0,3.0),0.0,Color(-1,-1,-1))
``````

`mod` のシンボルを使ったカリー化版として `=>=` がある。これは `Turtle => Turtle` 関数を生成する:

``````scala> val incX = turtleX =>= {_ + 1.0}
incX: Turtle => scalaz.Id.Id[Turtle] = <function1>

scala> incX(t0)
res26: scalaz.Id.Id[Turtle] = Turtle(Point(3.0,3.0),0.0,Color(-1,-1,-1))
``````

これで内部値の変化を事前に記述して、最後に実際の値を渡すことができた。これは何かに似てない?

### State モナドとしての Lens

これは状態遷移だと思う。実際、`Lens``State` は両方とも不変データ構造を使いながら命令形プログラミングを真似ているし相性がいいと思う。`incX` をこう書くこともできる:

``````scala> val incX = for {
x <- turtleX %= {_ + 1.0}
} yield x
incX: scalaz.StateT[scalaz.Id.Id,Turtle,Double] = scalaz.StateT\$\$anon\$7@38e61ffa

scala> incX(t0)
res28: (Turtle, Double) = (Turtle(Point(3.0,3.0),0.0,Color(-1,-1,-1)),3.0)
``````

`%=` メソッドは `Double => Double` 関数を受け取って、その変化を表す `State` モナドを返す。

`turtleHeading``turtleY` も作ろう:

``````scala> val turtleHeading = Lens.lensu[Turtle, Double] (
(a, value) => a.copy(heading = value),
)

scala> val pointY = Lens.lensu[Point, Double] (
(a, value) => a.copy(y = value),
_.y
)
pointY: scalaz.Lens[Point,Double] = scalaz.LensTFunctions\$\$anon\$5@ddede8c

scala> val turtleY = turtlePosition >=> pointY
``````

これはボイラープレートっぽいので嬉しくない。だけど、これで亀を前進できる! 一般的な `%=` の代わりに、Scalaz は `Numeric` な Lens に対して `+=` などの糖衣構文も提供する。具体例で説明する:

``````scala> def forward(dist: Double) = for {
x <- turtleX += dist * math.cos(heading)
y <- turtleY += dist * math.sin(heading)
} yield (x, y)
forward: (dist: Double)scalaz.StateT[scalaz.Id.Id,Turtle,(Double, Double)]

scala> forward(10.0)(t0)
res31: (Turtle, (Double, Double)) = (Turtle(Point(12.0,3.0),0.0,Color(-1,-1,-1)),(12.0,3.0))

scala> forward(10.0) exec (t0)
res32: scalaz.Id.Id[Turtle] = Turtle(Point(12.0,3.0),0.0,Color(-1,-1,-1))
``````

これで `copy(position = ...)` を一回も使わずに `forward` 関数を実装できた。これは便利だけど、ここまで来るのに準備も色々必要だったから、それはトレードオフだと言える。`Lens` は他にも多くのメソッドを定義するけど以上で十分使い始められると思う。並べて見てみる:

``````sealed trait LensT[F[+_], A, B] {
def get(a: A)(implicit F: Functor[F]): F[B] =
F.map(run(a))(_.pos)
def set(a: A, b: B)(implicit F: Functor[F]): F[A] =
F.map(run(a))(_.put(b))
/** Modify the value viewed through the lens */
def mod(f: B => B, a: A)(implicit F: Functor[F]): F[A] = ...
def =>=(f: B => B)(implicit F: Functor[F]): A => F[A] =
mod(f, _)
/** Modify the portion of the state viewed through the lens and return its new value. */
def %=(f: B => B)(implicit F: Functor[F]): StateT[F, A, B] =
mods(f)
/** Lenses can be composed */
def compose[C](that: LensT[F, C, A])(implicit F: Bind[F]): LensT[F, C, B] = ...
/** alias for `compose` */
def <=<[C](that: LensT[F, C, A])(implicit F: Bind[F]): LensT[F, C, B] = compose(that)
def andThen[C](that: LensT[F, B, C])(implicit F: Bind[F]): LensT[F, A, C] =
that compose this
/** alias for `andThen` */
def >=>[C](that: LensT[F, B, C])(implicit F: Bind[F]): LensT[F, A, C] = andThen(that)
}
``````

### Lens 則

Seth さん曰く:

Lens 則は常識的な感覚

(0. 2度 get しても、同じ答が得られる) 1. get して、それを set しても何も変わらない。 2. set して、それを get すると、set したものが得られる。 3. 2度 set して、get すると、2度目に set したものが得られる。

``````  trait LensLaw {
def identity(a: A)(implicit A: Equal[A], ev: F[Store[B, A]] =:= Id[Store[B, A]]): Boolean = {
val c = run(a)
A.equal(c.put(c.pos), a)
}
def retention(a: A, b: B)(implicit B: Equal[B], ev: F[Store[B, A]] =:= Id[Store[B, A]]): Boolean =
B.equal(run(run(a) put b).pos, b)
def doubleSet(a: A, b1: B, b2: B)(implicit A: Equal[A], ev: F[Store[B, A]] =:= Id[Store[B, A]]) = {
val r = run(a)
A.equal(run(r put b1) put b2, r put b2)
}
}
``````

### リンク

Jordan West さんによる An Introduction to Lenses in Scalaz という記事があって、飛ばし読みした感じだと Scalaz 6 っぽい。

Edward Kmett さんが Boston Area Scala Enthusiasts (BASE) で発表した Lenses: A Functional Imperative のビデオもある。

また続きは後で。