Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Van laarhoven lens

973 views

Published on

lens

Published in: Engineering
  • Be the first to comment

Van laarhoven lens

  1. 1. Lens の基本と基礎2017/02/26 Scala Matsuri 2017 - day 2
  2. 2. お前誰だよ? Naoki Aoyama マーベリック株式会社 アドテク系Scala エンジニア コミッター Twitter: , GitHub: Monocle @AoiroAoino @aoiroaoino
  3. 3. Lens ってご存知ですか?
  4. 4. Scala でLens を使うモチベーション ネストした構造に対するcopy メソッドのネスト地獄 様々なデータを共通のインターフェースで操作できる 非侵入的に外側から追加できる 特定のアーキテクチャに依存しない概念 汎用的に使える などなど
  5. 5. Lens って何? getter/setter を抽象的にしたもの purely functional reference
  6. 6. 実は 余状態コモナド余代数 (Costate Comonad Coalgebra)
  7. 7. getter/setter ってなんだっけ? (広く一般的な意味で)オブジェクト指向言語におい て、あるfield の値を取得するメソッドがgetter で、field に値をセットするのがsetter。(広く一般的な意味で) クラスのメソッドとして定義され、getFoo、setFoo と名 付けられる事が多い、おなじみの例のアレ。
  8. 8. getter/setter 再考 特定のclass に依存しない、汎用的な関数と捉えてみる
  9. 9. getter の仕事とは オブジェクトからある値を取り出す 素直にシグネチャとして表してみると… オブジェクトS からある値A を取り出す S => A // getter def get[S, A]: S => A
  10. 10. setter の仕事とは オブジェクトの一部をある値に変更する 素直にシグネチャとして表してみると… オブジェクトS の一部をある値A に変更する S => A => S // setter def set[S, A]: S => A => S
  11. 11. あるオブジェクトS の各field に対して前述のような getter/setter が存在した時、それらを使って汎用的な get/set メソッドの提供を考える。 ↓↓↓ その仕組みを実現するものをLens と呼ぶ。
  12. 12. get/set Lens class Lens[S, A]( getter: S => A, setter: S => A => S ) { def get(s: S): A = getter(s) def set(s: S, a: A): S = setter(s)(a) def modify(s: S, f: A => A): S = set(s, f(get(s))) }
  13. 13. get/set Lens getter/setter をコンストラクタの引数として与える。 class Lens[S, A]( getter: S => A, setter: S => A => S ) {
  14. 14. Lens の値を定義する case class User(id: Int, name: String) // User#id 対 Lens val _id = new Lens[User, Int]( _.id, // getter user => newId => user.copy(id = newId) // setter ) // User#name 対 Lens val _name = new Lens[User, String]( _.name, // getter user => newName => user.copy(name = newName) // setter )
  15. 15. 使い方 val user1 = User(100, "John Doe") _id.get(user1) // res: Int = 100 _name.get(user1) // res: String = John Doe _name.set(user1, "Naoki Aoyama") // res: User = User(100,Naoki Aoyama) _name.modify(user1, _.toUpperCase) // res: User = User(100,JOHN DOE)
  16. 16. Lens Laws Lens には満たすべき法則がある。 get/set set(s, get(s)) == s set/get get(set(s, a)) == a set/set set(set(s, a1), a2) == set(s, a2)
  17. 17. ネストしたデータが対象の時は? ネストした構造に対するcopy メソッドのネスト地獄を どう解決するか。
  18. 18. Lens の合成を考える class Lens[S, A](getter: S => A, setter: S => A => S) { def get(s: S): A = getter(s) def set(s: S, a: A): S = setter(s)(a) def modify(s: S, f: A => A): S = set(s, f(get(s))) def compose[B](other: Lens[A, B]): Lens[S, B] = new Lens( s => other.get(this.get(s)), // getter s => b => this.set(s, other.set(this.get(s), b)) //setter ) }
  19. 19. Lens の合成を考える def compose[B](other: Lens[A, B]): Lens[S, B] = new Lens( s => other.get(this.get(s)), // getter s => b => this.set(s, other.set(this.get(s), b)) //setter )
  20. 20. 試してみよう case class Message(user: User, body: String) // Message#id 対 Lens val _body = ... // Message#user 対 Lens val _user: Lens[Message, User] = new Lens( _.user, message => newUser => message.copy(user = newUser) )
  21. 21. Before val message = Message(User(100, "John Doe"), "Hello") message.user // res: User = User(100,John Doe) message.user.name // res: String = John Doe message.copy( user = user.copy( name = "aoino" ) ) // res: Message = Message(User(100,aoino), Hello) message.copy( user = user.copy( name = message.user.name.toUpperCase ) ) // res: Message = Message(User(100,JOHN DOE),Hello)
  22. 22. A er val message = Message(User(100, "John Doe"), "Hello") _user.get(message) // res: User = User(100,John Doe) (_user compose _name).get(message) // res: String = John Doe (_user compose _name).set(message, "aoino") // res: Message = Message(User(100,aoino),Hello) (_user compose _name).modify(message, _.toUpperCase) // res: Message = Message(User(100,JOHN DOE),Hello)
  23. 23. ここまでのまとめ Lens の基本的な概念を完全に理解した
  24. 24. Lens の種類
  25. 25. Lens にはコンセプトや実装に応じていくつか種類がある get/set lens get/modify lens Iso lens Store Comonad Lens van Laarhoven lens and so on ...
  26. 26. van Laarhoven Lens
  27. 27. van Laarhoven Lens とは? 2009 年7 月にTwan van Laarhoven さんが書いた の アイディアが元になったLens で、Haskell の やScala の のコンセプトの基礎になってる。 blog lens Monocle
  28. 28. 型のイメージは以下の通り。 type Lens[F[_]: Functor, S, A] = S => (A => F[A]) => F[S] 実際に定義すると abstract class Lens[F[_]: Functor, S, A] { def run(s: S)(f: A => F[A]): F[S] }
  29. 29. type Lens s a = forall f. Functor f => (a -> f a) -> s -> f s
  30. 30. さて、閑話休題
  31. 31. traverse 関数はご存知ですか? def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
  32. 32. 例えば、F をList、G をOption とすると、traverse 関数は リスト内の値にf を適用し、全てSome だった場合には f の適用結果をSome に包んで返し 一つでもNone の場合はNone を返します。 def isEven(v: Int): Option[Int] = if (v % 2 == 0) Some(v) else None List(2, 4, 5).traverse(isEven) // res: Option[List[Int]] = None List(2, 4, 6).traverse(isEven) // res: Option[List[Int]] = Some(List(2, 4, 6)) ※ 上記コードはScalaz を使用しています。
  33. 33. 閑話休題おわり
  34. 34. さて、まずはvan Laarhoven lens を理解する為に getter/setter を再考してみましょう。
  35. 35. setter の再考
  36. 36. setter の再考 冒頭に出てきたsetter の型は以下の通り。 def setter: S => A => S これはmodify メソッドの特殊形と考えられる。 なので、modify のsignature を取り入れて def setter: S => (A => A) => S と、改めて定義する。
  37. 37. この型に見覚えない?
  38. 38. trait Functor[F[_]] { // F[A] => (A => B) => F[B] def map[A, B](fa: F[A])(f: A => B): F[B] }
  39. 39. つまり、我々はset/modify をするのに各field に対する Functor#map が欲しいのだ。
  40. 40. しかし、Functor のinstance をclass のfield 毎にそれぞれ 定義することは現実的ではない...
  41. 41. Functor#map はTraverse#traverse で定義できる trait Traverse[F[_]] extends Functor[F] { // F[A] => (A => G[B]) => G[F[B]] def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B type Id[A] = A // Id Applicative instance 自明 implicit def idApplicative[A] = new Applicative[Id] { ... } // F[A] => (A => Id[B]) => Id[F[B]] def map[A, B](fa: F[A])(f: A => B): F[B] = traverse[Id, A, B](fa)(f) }
  42. 42. つまり、このtraverse の部分を同等な関数に置き換える ことで、Functor#map のような関数を得られる。この F[A], F[B] をS と置いてSetter という名前をつけよう。 type Setter[S, A] = Lens[Id, S, A] このSetter[S, A] を用いてset/modify メソッドが作れる。 // Setter[S, A] => S => (A => A) => S def modify[S, A](setter: Setter[S, A])(s: S)(f: A => A): S = setter.run(s)(f) // Setter[S, A] => S => A => S def set[S, A](setter: Setter[S, A])(s: S)(a: A): S = setter.run(s)(_ => a)
  43. 43. getter の再考
  44. 44. getter の再考 冒頭に出てきたgetter の型は以下の通り。 def getter: S => A 取得するだけでなく、関数を適用した結果を返すように def getter: S => (A => A) => A と、改めて定義する。
  45. 45. この型に見覚えない?
  46. 46. trait Foldable[F[_]] { // F[A] => (A => B) => B def foldMap[A, B](fa: F[A])(f: A => B)(implicit mb: Monoid[B]): B }
  47. 47. つまり、我々はget をするのに各field に対する Foldable#foldMap が欲しいのだ。
  48. 48. しかし、Foldable のinstance をclass のfield 毎にそれぞ れ定義することは現実的ではない...
  49. 49. Foldable#foldMap はTraverse#traverse で定義できる trait Traverse[F[_]] extends Functor[F] { // F[A] => (A => G[B]) => G[F[B]] def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B // Const Applicative instance 自明 case class Const[A, B](runConst: A) implicit def monoidApplicative[A](A: Monoid[A]) = new Applicative[({type λ[X] = Const[A, X]})#λ] { ... } // F[A] => (A => B) => B def foldMap[A, B](fa: F[A])(f: A => B)(implicit mb: Monoid[B]): B = traverse[({type λ[X] = Const[B, X]})#λ, A, Nothing](fa)(f) }
  50. 50. Setter と同様にtraverse の部分を同等な関数に置き換え ることで、Foldable#foldMap のような関数を得られる。 このF[A] をS と置いてGetter という名前をつけよう。 type Getter[S, A] = Lens[({type F[X] = Const[A, X]})#F, S, A] このGetter[S, A] を用いてget メソッドが作れる。 // Getter[S, A] => S => A def get[S, A](getter: Getter[S, A])(s: S): A = getter.run(s)(a => Const(a)).getConst
  51. 51. 試しにUser#name に対するLens を定義してみる。 def _name[F[_]: Functor] = new Lens[F, User, String] { def run(user: User)(f: String => F[String]): F[User] = Functor[F].map(f(user.name))(n => user.copy(name = n)) }
  52. 52. 本当に動くか試してみよう!!
  53. 53. さて、我々は似たようなsignature を持つGetter/Setter を 手に入れた。これらを並べて見てみよう。 type Setter[S, A] = S => (A => Id[A]) => Id[S] type Getter[S, A] = S => (A => Const[A, A]) => Const[A, S] Const とId の部分をFunctor のinstance を要求する型変 数に置き換えられそう。
  54. 54. これでvan Laarhoven lens の定義を理解できましたね? type Lens[F[_]: Functor, S, A] = S => (A => F[A]) => F[S]
  55. 55. type Lens s a = forall f. Functor f => (a -> f a) -> s -> f s
  56. 56. しかし、ここで一つ疑問が湧きませんか?
  57. 57. Q: van Laarhoven lens はうまく合成できるのだろうか?
  58. 58. Getter もSetter も中身はtraverse と同等の関数ですね。 なので、代表してtraverse の合成を見てみましょう。
  59. 59. しかし、Scala でのtraverse の(関数合成の意味での) compose は辛いので、Haskell で試します...
  60. 60. f にId/Const が入ります。構造のネストする順番と合成 の順序が一致し、左から右へと辿れますね。 traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) traverse . traverse :: (Applicative f, Traversable t, Traversable t1) => (a -> f b) -> t (t1 a) -> f (t (t1 b)) traverse . traverse . traverse :: (Applicative f, Traversable t, Traversable t1, Traversable t2) => (a -> f b) -> t (t1 (t2 a)) -> f (t (t1 (t2 b))) おやおや?このHaskell の関数合成に使う(.) 演算子が、 Scala やJava などの(.) 演算子に見えませんか?
  61. 61. ちなみに、Haskell でvan Laarhoven Lens の合成はこう書 ける。 ghci> set (_2 . _1) 42 ("hello",("world","!!!")) ("hello",(42,"!!!"))
  62. 62. A: 合成もできる!
  63. 63. まとめ Lens の基本を完全に理解した Lens には種類がある van Laarhoven lens をちょっと理解した

×