SlideShare ist ein Scribd-Unternehmen logo
1 von 77
Downloaden Sie, um offline zu lesen
Lens で Haskell をもっと格好良く!




          for 2013/3/31 ekmett 勉強会
                          ちゅーん
私はだあれ?

   山手圏内で活動している
    下っ端プログラマ
   仕事の疲れは Haskell で癒す
    日曜 Haskeller
   Free モナドとか好きです



   あと SDVX とか好き。音ゲーマーは Join me!
本日のメニュー

   Lens とは何か
   Lens でタプルを便利にする
   任意のデータ型を Lens で使う
   Lens の仕組ってどーなってんの?
   Lens の便利な関数紹介
   まとめ的な何か


                        ※ ゆるふわ注意
Lens とは何か
Lens とは・・・



 タプルを始めとした任意のデータ構造の要素に対
 する Setter や Getter を得るためのライブラリ
 Haskell で、 Java や C# といったオブジェクト指向
 手続きプログラミングに似た記法で、要素にアクセ
 スできるようになる
Lens でタプルを便利にする
タプルの要素を取り出す方法

   パターンマッチで取得する

    f (a, _, _) = a * 2

   関数を定義して使う

    first   (x, _, _) = x
    secound (_, x, _) = x
    third   (_, _, x) = x

    g v = (first v) * 2
ネストした内側の要素を取り出す

   パターンマッチするとなんかキモい
    全パターン網羅するとか無理ぽ

    f ((_, (_, _, x)), _, _) = x


   関数合成を使えば綺麗&簡単
    third.snd.first $ ((1, (1, 1, 999)), 1, 1)
                                        -- => 999
任意の位置の値を置き換えるには

   パターンマッチを使って関数を書く。めんどい

    secondTo999 (x, _, y) = (x, 999, y)


   関数を定義する
    setFirst x (_, a, b) = (x, a, b)
    setSecond x (a, _, b) = (a, x, b)
    setThird x (a, b, _) = (a, b, x)
    f'' = setSecond 999 (1, 2, 3) -- => (1,999,3)
ネストした内側の値を変更
   素直に関数定義・・・超キモい
f x ((a, (b, c, _)), d, e) = ((a, (b, c, x)), d, e)


   関数合成ではできない
ghci> :t setFirst.setThird
setFirst.setThird
  :: a -> (t, t2, t3) ->
     ((t4, t5, t1) -> (t4, t5, a), t2, t3)

   こうすればちょっとはマシ
f x t = setThird (setFirst x $ third t) t
と に か く 超 不 満

   値の取得と変更の識別子が異なる
   タプルの要素数が異なると同じ識別子が使えない
   構造がネストすると値の設定が超めんどい


    Java とか C# のような手続き言語で
    public 変数にアクセスするみたいに
    もっとスマートに構造を扱う事はできないの?
と に か く 超 不 満

   値の取得と変更の識別子が異なる
   タプルの要素数が異なると同じ識別子が使えない
   構造がネストすると値の設定が超めんどい

       そこで Lens ですよ!!
    Java とか C# のような手続き言語で
    public 変数にアクセスするみたいに
    もっとスマートに構造を扱う事はできないの?
Lens のインストール
   Cabal でいっぱつ

$ cabal install lens


   Hackage から直接アーカイブを取得


    http://hackage.haskell.org/package/lens-3.9.0.2


    コンパイルに時間がかかるので、
    すごい H 本か TaPL あたりを読んでゆっくり待とう
Lens を import

   Haskell のソースコードに

Import Control.Lens



   あるいは、 ghci で

:m Contorol.Lens
Lens で要素の取得

    (^.) と _1, _2, _3,... で簡単に取り出し

    ("Foo", "Bar", "Buz")^._1 -- => "Foo"
    ("Foo", "Bar", "Buz")^._2 -- => "Bar"
    ("Foo", "Bar", "Buz")^._3 -- => "Buz"

    _1 ~ _9 まで別々の型クラスに定義されているので
     要素数が異なるタプルに対しても同じように使える

    ('A', 'B', 'C', 'D', 'E')^._3 -- => 'C'
ネストしたタプルから要素を取り出す
    _1 ~ _9 は (.) で関数合成しても同じ型
  ghci> :t _1
  _1
     :: (Functor f, Field1 s t a b, Indexable Int p) =>
        p a (f b) -> s -> f t
  ghci> :t _1._2
  _1._2
     :: (Functor f, Field2 s1 t1 a b, Field1 s t s1 t1,
         Indexable Int p) =>
        p a (f b) -> s -> f t

    _1 ~ _9 を (.) で合成して、ネストした
     複雑なタプルの内側の値をピンポイントで取り出し
(100, 200, (310, (321, 322, 323, 999, 325), 330), 400)^._3._2._4
                                          -- => 999
Lens でタプルの値を変更
    (.~) に _1 ~ _9 と、任意の値を適用
    ghci> :t _2.~"Foo"
    _2.~"Foo" :: Field2 s t a [Char] => s -> t

    要素が2つ以上のタプルは
     Field2 型クラス s のインスタンス
    ghci> :i Field2
    class Field2 s t a b | s -> a, t -> b, s b -> t, t a -> s where
      _2 :: (Indexable Int p, Functor f) => p a (f b) -> s -> f t
        -- Defined in `Control.Lens.Tuple'

    ...

      -- Defined in   `Control.Lens.Tuple'
    instance Field2   (a, b, c) (a, b', c) b b'
      -- Defined in   `Control.Lens.Tuple'
    instance Field2   (a, b) (a, b') b b'
      -- Defined in   `Control.Lens.Tuple'
Lens でタプルの値を変更
    (.~) に _1 ~ _9 と、任意の値を適用
    ghci> :t _2.~"Foo"
    _2.~"Foo" :: Field2 s t a [Char] => s -> t


    _2.~”Foo” にタプルを適用すると
     二つ目の要素が ” Foo” に変更される

    _2.~"Foo" $ (1, 2)       -- => (1,"Foo")
    _2.~"Foo" $ (1, 2, 3)    -- => (1,"Foo",3)
    _2.~"Foo" $ (1, 2, 3, 4) -- => (1,"Foo",3,4)
Lens でタプルの値を変更
   勿論、 _1 ~ _9 を関数合成しても良い
    _4._2.~999 $ (1,2,3,(1,2,3),5)
                         -- => (1,2,3,(1,999,3),5)

   ($) の代わりに flip ($) と外延的等価な (&) を使う
    ghci> :i (&)
    (&) :: a -> (a -> b) -> b
        -- Defined in `Control.Lens.Combinators'
    infixl 1 &

    (1,2,3,(1,2,3),5)&_4._2 .~ 999
                         -- => (1,2,3,(1,999,3),5)
Lens でタプルの値を変更
   勿論、 _1 ~ _9 を関数合成しても良い
    _4._2.~999 $ (1,2,3,(1,2,3),5)
                         -- => (1,2,3,(1,999,3),5)

   ($) の代わりに flip ($) と外延的等価な (&) を使う
         Java や C# の代入文そっくり!
    ghci> :i (&)
    (&) :: a -> (a -> b) -> b
        -- Defined in `Control.Lens.Combinators'
    infixl 1 &

    (1,2,3,(1,2,3),5)&_4._2 .~ 999
                         -- => (1,2,3,(1,999,3),5)
ここまでのまとめ

    タプルの操作には不満がまんまん
    でも Lens を使えば・・・
   値の取得も変更も同じ識別子で参照できる
   ネストしたタプルの値の変更も
    手続き言語の代入感覚でらくらく書ける

    それでいてしっかり型安全 ( これ重要)

      タプル以外の型もこんな風にできない?→
任意のデータ型を Lens で使う
Point 型 /Line 型を作る
    次のような型を作る
    data Point = Point {
      x :: Int,
      y :: Int
      } deriving (Show, Eq)

    data Line = Line {
      startPoint :: Point,
      endPoint :: Point
      } deriving (Show, Eq)
    次の値を例に色々考えてみよう
    sampleLine = Line {
      startPoint = Point { x = 100, y = 150 },
      endPoint   = Point { x = 200, y = 250 }
      }
Point 単位の操作は簡単

   取得
    startPoint sampleLine
       -- => Point {x = 100, y = 150}
    endPoint sampleLine
       -- => Point {x = 200, y = 250}

   置き換え

    SampleLine {
       endPoint = Point { x = 1000, y = 1500 }}
では、座標単位の操作は?

    取得は関数合成を使えば良い

      x . endPoint $ sampleLine -- => 200

    置き換えは・・・いまいち分りづらい

    sampleLine {
      endPoint = (endPoint sampleLine) { x = 999 } }

      -- => Line {
              startPoint = Point {x = 100, y = 150},
              endPoint   = Point {x = 999, y = 250}}
では、座標単位の操作は?

    取得は関数合成を使えば良い

      x . endPoint $ sampleLine -- => 200

    置き換えは・・・いまいち分りづらい
            よし、 Lens を使おう!
    sampleLine {
      endPoint = (endPoint sampleLine) { x = 999 } }

      -- => Line {
              startPoint = Point {x = 100, y = 150},
              endPoint   = Point {x = 999, y = 250}}
Point 型 /Line 型を Lens にする
   フィールド名の前に” _” を付加し
    『 makeLenses '' 型名』 と記述する
    data Point = Point {
      _x :: Int,
      _y :: Int
      } deriving (Show, Eq)

    makeLenses ''Point
    data Line = Line {
      _startPoint :: Point,
      _endPoint :: Point
      } deriving (Show, Eq)
    makeLenses ''Line

    ※ コンパイルのためには GHC 拡張の TemplateHaskell を
     有効にしておく必要がある
Point 型 /Line 型を Lens にする
    フィールド名から” _” を抜いた識別子を使って
     (^.) や (.~) で要素にアクセスできるようになる
    sampleLine^.startPoint     --   =>   Point {_x = 100, _y = 150}
    sampleLine^.endPoint       --   =>   Point {_x = 200, _y = 250}
    sampleLine^.startPoint.x   --   =>   100
    sampleLine^.endPoint.y     --   =>   250
    sampleLine&startPoint.x.~999
      -- => Line {
      --      _startPoint = Point {_x = 999, _y = 150},
      --      _endPoint = Point {_x = 200, _y = 250}}
    sampleLine&endPoint.x.~999
      -- => Line {
      --      _startPoint = Point {_x = 100, _y = 150},
      --      _endPoint = Point {_x = 999, _y = 250}}


                           カッコイイ!!
こんな場合はどうなる?
   型変数が含まれる型でも
    data Foo a = Foo {
      _hoge :: a,
      _piyo :: Int } deriving (Show, Eq)

    makeLenses ''Foo
    sampleFoo = Foo { _hoge = "Hello!", _piyo = 100 }


   もちろん大丈夫☆(ゝ ω ・) v
    sampleFoo^.hoge -- => "Hello!"
    sampleFoo^.piyo -- => 100
    sampleFoo&hoge.~True
                      -- => Foo {_hoge = True, _piyo = 100}
    sampleFoo&piyo.~999
                      -- => Foo {_hoge = "Hello!", _piyo = 999}
ここまでのまとめ
    自分で作った型も Lens で操作したい!
    でも型とかややこしそうだし面倒では?
   TemplateHaskell の力を借りて
    ちょ〜簡単に Lens になるよ
   型変数を含む場合も無問題!

    それでいてしっかり型安全 ( 大事なことなのでn (ry


        いったいどういう仕組みなんだろう?→
Lens の仕組ってどーなってんの?
Setter を作ろう

   単純に 2 要素のタプルの 2 つめの要素を任意の
    値に置き換える関数を考えると、次のような型にな
    る

       f :: a -> (b, c) -> (b, a)

   これだけではつまらないので、一つ目の引数を関
    数で取るようにする

    f :: (a -> b) -> (c, a) -> (c, b)
Setter を作ろう

   単純に 2 要素のタプルの 2 つめの要素を任意の
    値に置き換える関数を考えると、次のような型にな
    る

       f :: a -> (b, c) -> (b, a)
         値を x に置き換えたい場合は
         const x を適用すれば良い
    これだけではつまらないので、一つ目の引数を関
    数で取るようにする

    f :: (a -> b) -> (c, a) -> (c, b)
ところで

   この型、何かと似てない?

    f :: (a -> b) -> (c, a) -> (c, b)



      fmap :: Functor f => (a -> b) -> f a -> f b
                   とそっくり・・・
ところで

   この型、何かと似てない?

     f :: (a -> b) -> (c, a) -> (c, b)

    ※ 衆知のとおり、 2 値のタプルは Functor になっ
    ていて、次のような事ができる

    fmap (*2) ("Hey!", 5) -- => ("Hey!",10)

    しかし Functor では一つ目の要素は操作できない
    さて、どうしよう?
fmap のもうひとつの実装
    Data.Traversable で定義されている Traversable
     型クラスで、次の型を持つ traverse 関数が定義さ
     れている
    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)


    同モジュールの fmapDefault 関数は、 traverse 関
     数を用いた fmap の別実装

    fmapDefault :: Traversable t => (a -> b) -> t a -> t b
    fmapDefault f = getId . traverse (Id . f)
fmap のもうひとつの実装
    Data.Traversable で定義されている Traversable
     型クラスで、次の型を持つ traverse 関数が定義さ
     れているData.Functor.Identity の定義に同じ
        Id は
              newtype Id a = Id { getId :: a }
    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
          Functor と Applicative のインスンタンス
    同モジュールの fmapDefault 関数は、 traverse 関
     数を用いた fmap の別実装

    fmapDefault :: Traversable t => (a -> b) -> t a -> t b
    fmapDefault f = getId . traverse (Id . f)
fmapDefault の
動作を決めるのは traverse 関数
    なら、 traverse を別の関数と差し替えれば別の動
     きをするんじゃなイカ?というわけで ...
     fmapDefault から traverse を外出しした over 関数
     を定義すると、次のような型になる
    over
      :: ((a1 -> Id b) -> a -> Id c) -> (a1 -> b) -> a -> c
    over l f = getId . l (Id . f)
    ここで、第一引数の型に対し Setter という別名を
     付けよう
    type Setter s t a b = (a -> Id b) -> s -> Id t
fmapDefault の
動作を決めるのは traverse 関数
    なら、 traverse を別の関数と差し替えれば別の動
     きをするんじゃなイカ?というわけで ...
     fmapDefault から traverse を外出しした over 関数
     を定義すると、次のような型になる
    over
      :: Setter a c a1 b -> (a1 -> b) -> a -> c
    over l f = getId . l (Id . f)
    ここで、第一引数の型に対し Setter という別名を
     付けよう
    type Setter s t a b = (a -> Id b) -> s -> Id t
fmapDefault の
動作を決めるのは traverse 関数
    なら、 traverse を別の関数と差し替えれば別の動
     きをするんじゃなイカ?というわけで ...
             これにより、 over の型がこう書ける
     fmapDefault から traverse を外出しした over 関数
     を定義すると、次のような型になる
    over
      :: Setter a c a1 b -> (a1 -> b) -> a -> c
    over l f = getId . l (Id . f)
    ここで、第一引数の型に対し Setter という別名を
     付けよう
    type Setter s t a b = (a -> Id b) -> s -> Id t
fmapDefault の
動作を決めるのは traverse 関数
    なら、 traverse を別の関数と差し替えれば別の動
     きをするんじゃなイカ?というわけで ...
     fmapDefault から traverse を外出しした over 関数
     を定義すると、次のような型になる
    over
      :: Setter a c a1 b -> (a1 -> b) -> a -> c
    over l f = getId . l (Id . f)
    ここで、第一引数の型に対し Setter という別名を
     付けよう
    type Setter s t a b = (a -> Id b) -> s -> Id t
fmapDefault の
動作を決めるのは traverse 関数
    なら、 traverse を別の関数と差し替えれば別の動
     きをするんじゃなイカ?というわけで ...
     fmapDefault から traverse を外出しした over 関数
     を定義すると、次のような型になる
    over
      :: Setter a c a1 b -> (a1 -> b) -> a -> c
    over l f = getId . l (Id . f)
    ここで、第一引数の型に対し Setter という別名を
          当然、 traverse 関数を適用すれば
     付けよう   fmapDefault と同値になる

       さらに何かしら Setter 型の関数を引数に取る事により
    type Setter s t a b = (a -> Id b) -> s -> Id t
             fmap と似た別の関数を得る事ができる
_1, _2 を実装するには?
    最終的に欲しい型

    Over _1 ::              (a -> b) -> (a, v) -> (b, v)

    over の型を読み替え
Over :: Setter (a, v) (b, v) a b
                         -> (a -> b) -> (a, v) -> (b, v)

    _1 の型
_1 :: Setter (a, v) (b, v) a b
          -- つまり
_1 ::    (a -> Id b) -> (a, v) -> Id (b, v)
実際にやってみる
    導きだした型を満足させるよう _1, _2 を実装

_1 :: Setter (a, v) (b, v) a b
_1 f (x, y) = Id (getId . f $ x, y)

_2 :: Setter (v, a) (v, b) a b
_2 f (x, y) = Id (x, getId . f $ y)

    任意の要素に fmap できるようになる!
    (over _1) (*2) (50, 50) -- => (100,50)
    (over _2) (*2) (50, 50) -- => (50,100)
こうなれば後は簡単

    (.~) は次のようにして簡単に再実装できる

    (.~) :: Setter s t a b -> b -> s -> t
    a .~ v = over a (const v)

    Lens と同じ書き方で要素を変更できるようになる


    _1.~999 $ (1, 2) => (999,2)
    _2.~999 $ (1, 2) => (1,999)
それじゃぁ次は Getter だ!
   2 値のタプルからの値の取得は次のような型をイ
    メージできる
              f :: (a, b) -> b
   単に取り出すだけでなく、何か関数を適用して返す
    ようにしてみると・・・
       f :: (a -> b) -> (c, a) -> b
   これは Data.Foldable で定義されている foldMap
    関数とそっくり

foldMap
  :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
Traversable の foldMapDefault
   FoldMapDefault の定義が Data.Traversable
    に!
foldMapDefault
  :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldMapDefault f = getConst . traverse (Const . f)




       Const は Data.Functor.Constant に定義
      newtype Const a b = Const {getConst :: a}

      Foldable や Applicative 等のインスンタンス
同じようにして traverse を外に出す

    foldMapDefault の実装から traverse を取り出し
     foldMapOf 関数を定義
    foldMapOf
      :: ((a1 -> Const b1 b2) -> a -> Const c b)
         -> (a1 -> b1) -> a -> c
    foldMapOf l f = getConst . l (Const . f)


    第一引数の関数の型に別名を付けてみる
    type Getting r s a =
                 (a -> Const r a) -> s -> Const r s
アクセサの定義、
foldMapOf への適用
    改めて、 2 値のタプルに対する _1, _2 を次のよう
     に定義
    _1 :: Getting r (a, s) a
    _1 f (x, _) = Const (getConst . f $ x)

    _2 :: Getting r (s, a) a
    _2 f (_, y) = Const (getConst . f $ y)
    foldMapOf と組み合わせて任意の場所の要素を
     foldMap
    (foldMapOf _1) (*2) (100, 1000) -- => 200
    (foldMapOf _2) (*2) (100, 1000) -- => 2000
(^.) の実装も超簡単

(^.) :: s -> Getting a s a -> a
v ^. l = (foldMapOf l) id v

           値をそのまま取り出したいのだから
          引数に対して何もしない関数 id :: a -> a
              を、適用してやれば良い



(111, 222)^._1 -- => 111
(111, 222)^._2 -- => 222
Setter と Getting
    どちらも traverse 関数を元に定義された型なのだ
     から、揃える事はできないだろうか?
    type Getting r s a =
                 (a -> Const r a) -> s -> Const r s
    type Setter s t a b =
                 (a -> Id      b) -> s -> Id      t

    Getting の型変数を Setter に合わせて変えてみる
type Getting s t a b = forall m .Monoid m =>
             (a -> Const m b) -> s -> Const m t
type Setter s t a b =
             (a -> Id      b) -> s -> Id      t
Setter と Getting
    どちらも traverse 関数を元に定義された型なのだ
     から、揃える事はできないだろうか?
    type Getting r s a =
                  型定義に登場しない型変数 m
                 (a -> Const r a) -> s -> Const r s
    type Setter s t a b =
            コンパイルのため、 FoldMap の実装に合せ
                 (a -> Id      b) -> s -> Id      t
                Monoid を要求するようにしておく
                   でもあまり嬉しくない制約
    Getting の型変数を Setter に合わせて変えてみる
type Getting s t a b = forall m .Monoid m =>
             (a -> Const m b) -> s -> Const m t
type Setter s t a b =
             (a -> Id      b) -> s -> Id      t
Id も Const も Functor !

    従って、次の赤字の部分は、 Functor を要求する
     型変数に置き換えることができる
    type Getting s t a   b = forall m .Monoid m =>
                 (a ->   Const m b) -> s -> Const m t
    type Setter s t a    b =
                 (a ->   Id      b) -> s -> Id      t

    これで型宣言も一つに纏められる
     しかも Getter の Monoid も消えた!やったね!
    type Lens s t a b = forall f .Functor f =>
                 (a -> f       b) -> s -> f        t
_1, _2 を作り替える
   後は _1 と _2 を、それぞれ Lens 型に合うように実
    装
    _1 :: Lens (a, v) (b, v) a b
    _1 f (x, y) = fmap (x' -> (x', y)) (f x)

    _2 :: Lens (v, a) (v, b) a b
    _2 f (x, y) = fmap (y' -> (x, y')) (f y)

   ちゃんと使えるかどうか確認・・・バッチリ!

    (100, 200)^._1       -- => 100
    _1.~999 $ (100, 200) -- => (999,200)
traverse. traverse
   traverse 関数同士を関数合成するとこうなる
traverse
  :: (Control.Applicative.Applicative f, Traversable t) =>
     (a -> f b) -> t         a -> f (t           b)

traverse.traverse
  :: (Control.Applicative.Applicative f, Traversable t,
      Traversable t1) =>
     (a -> f b) -> t (t1     a) -> f (t (t1      b))

traverse.traverse.traverse
  :: (Control.Applicative.Applicative f, Traversable t,
      Traversable t1, Traversable t2) =>
     (a -> f b) -> t (t1 (t2 a)) -> f (t (t1 (t2 b)))

    合成しても性質が維持される!
Lnes 型の関数は traverse と同じ型

    なら _1 や _2 も同じ性質を持っているはず・・・

    _2       :: Functor f =>
    (a -> f b) -> (v,           a)    -> f (v,          b)

    _2._2    :: Functor f =>
    (a -> f b) -> (v, (v1,      a))   -> f (v, (v1,     b))

    _2._2._2 :: Functor f =>
    (a -> f b) -> (v, (v1, (v2, a))) -> f (v, (v1, (v2, b)))

     Lens が関数合成して使えるのは
     型を見れば当然の事だった!!
そんなワケで

    今回再実装したオレオレ Lens も
     _1 や _2 を関数合成して、ネストしたタプルの好き
     な要素にアクセスできるよっ


    _2._1.~"Lens" $ ("Hello", ((), "World"))
                            -- => ("Hello",("Lens","World"))

    ("Hello", ("Lens", "World"))^._2._1
                            -- => "Lens"
(注)

   今回の再実装で Id 、 Const という型を使ったが、
    実際の Lens の実装では Mutator 、 Accessor と
    いう別実装を用いている


    これは、型エラーが発生した時に、よりエラーの原
    因を特定しやすくするため。
ここまでのまとめ
    Lens の仕組みって凄い複雑そう・・・
    超人的な知能を持っていないと理解できない
    んじゃ?
   Traversable 型クラスの fmapDefault 関
    数 /foldMapDefault 関数から、型を中心に
    追っていけば自然と導き出せるよ!



           もっと Lens の事が知りたいな!→
Lens の便利な関数紹介
と、その前に・・・
   Control.Lens モジュール内では、 Lens と同じよう
    な型に様々な別名が付けられていて・・・
    ・ Lens
    ・ Getter
    ・ Setter
    ・ Fold
    ・ Action
                                ... 等々
    それぞれ型クラスの制約なんかが少しづつ違って
    いたりするので、必要に応じて Hackage を参照
foldMapOf 関数 / over 関数
    前の節で実装した foldMapOf 関数と over 関数は
     Lens モジュールをインポートしてそのまま使える
    (foldMapOf _2) (*100) (1, 2, 3) --=> 200

    (over _2) (*100) (1, 2, 3) -- => (1,200,3)

    (foldMapOf y) (*2) $ Point { _x = 100, _y = 200 }
                           -- => 400

    (over x) (*2) $ Point { _x = 100, _y = 200 }
                           -- => Point {_x = 200, _y = 200}

    %~ は over の中置バージョン
_2 %~ (*100) $ (1, 2, 3) -- => (1,200,3)
to 関数で関数適用

   to 関数を使えば、 (^.) で取得した値に対して関数
    適用できる

    (1, 2, 3)^._2.to (*100) -- => 200

   さらに関数合成を連ねて次のようにしても良い

    (1,(10,20),3)^._2.to swap._2.to (*100)
                             -- => 1000
Setter の演算子色々
   対象となる要素が Num 型クラスのインスタンスや
    Bool 等の特定の型であれば、それらに対して便利
    な演算子を使う事ができる。
-- 加算
(10, 20)&_1 +~ 100       -- => (110,20)
-- 減算
(10, 20)&_1 -~ 5         -- => (5,20)
-- 乗算
(10, 20)&_1 *~ 100       -- => (1000,20)
-- 除算
(10, 20)&_1 //~ 5        -- => (2.0,20)
--AND
(True, 1)&_1 &&~ False   -- => (False,1)
--OR
(True, 1)&_1 ||~ False   -- => (True,1)
(.=) と use 関数

   どちらも型クラス制約に MonadState クラスが含ま
    れている。
    状態系のモナドと組み合わせて使う関数。

ghci> :t (.=)
(.=) :: MonadState s m => ASetter s s a b -> b -> m ()

ghci> :t use
use
  :: MonadState s m =>
     Getting a s t a b -> m a
(.=) と use 関数
   (.=) や use の簡単な例:

    sample :: State Line ()
    sample = do
      -- (.=) で状態に代入
      startPoint .= Point { _x = 100, _y = 200 }
      endPoint.x .= 300

     -- 状態から値を取り出し
     sp <- use startPoint
     epx <- use $ endPoint.x

     return ()
各 Setter 演算子の
MonadState バージョン
   何処かの言語で見たような書き方ができる
    sample2 = do
      v %= (*100) --over

      v   += 10    -- 加算
      v   -= 10    -- 減算
      v   *= 10    -- 乗算
      v   //= 10   -- 除算

      b ||= True   --OR
      b &&= True   --AND

    ※ 「状態」に対してかなりやりたい放題できるように
    なるので乱用注意!
Getter のアクセス時に
モナドアクションを付加する Action
     (^.) の代わりに (^!) を使うと、 act 関数を使ってモ
      ナドのアクセサにモナドアクションを追加する事が
      できる
(("Foo", "Bar"), "Buz")^!_1.act (Just)._2 -- => Just "Bar"

(("Lens", "Hello"), "World!")^!_1.act (x -> print x >> return x).to swap
  -- => ("Hello","Lens") ※ 途中で ("Lens","Hello") を print



     次は Nothing になるかと思ったけど、 No instance
      エラーになった・・・ (´ ・ ω ・ `) ?
     (("Foo", "Bar"), "Buz")^!_1.act (const Nothing)._2
などなど

   Lens モジュールには Lens をより便利に使う仕組
    みが沢山用意されているので、 Hackage を一読し
    てみよう!



    http://hackage.haskell.org/package/lens-3.9.0.2
ところで・・・これは何だ・・・?
   Lens の構成図を見る
    と、さらに下層に ISO
    とか Equality とか
    ゆー輩がいます…が
         Lens

        ISO
         Equality


    勉強不足でご紹介で
    きないです orz
    ゴメンナサイ
まとめ的な何か
改めて Lens って何?

   レンズで覗くように複雑なデータ構造の内部に自在
    にアクセスする手段を提供してくれるライブラリ軍
   仕組みはやや複雑だけど Traversable を起点に
    考えていけば考え方はとっても明瞭
   単なるアクセサの域を超えた自在な操作を可能に
    するスーパーウルトラアルティメットアクセサ
   もうちょっと秘められたパワーがありそうです
大きいライブラリなので
尻込みしちゃうかもしれないけど
 Lens は全然怖くないよ!
!????
(((( ;゚ Д ゚ )))) こ・・・怖くねーし
Lens 全然怖くねーから・・・
さぁ、みんな Lens を使おう!




 ご清聴ありがとうございました!!

Weitere ähnliche Inhalte

Was ist angesagt?

プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法Takuya Akiba
 
What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?土岐 孝平
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 
goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?mori takuma
 
OpenStackで始めるクラウド環境構築入門
OpenStackで始めるクラウド環境構築入門OpenStackで始めるクラウド環境構築入門
OpenStackで始めるクラウド環境構築入門VirtualTech Japan Inc.
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜京大 マイコンクラブ
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?Moriharu Ohzu
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)Takeshi Yamamuro
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Masahito Zembutsu
 
FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎ken_kitahara
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたってTsuyoshi Matsudate
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021Hiroshi Tokumaru
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 TipsTakaaki Suzuki
 
OpenFOAMの壁関数
OpenFOAMの壁関数OpenFOAMの壁関数
OpenFOAMの壁関数Fumiya Nozaki
 
ブレソルでテラバイト級データのALTERを短時間で終わらせる
ブレソルでテラバイト級データのALTERを短時間で終わらせるブレソルでテラバイト級データのALTERを短時間で終わらせる
ブレソルでテラバイト級データのALTERを短時間で終わらせるKLab Inc. / Tech
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装infinite_loop
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Taku Miyakawa
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理KageShiron
 

Was ist angesagt? (20)

C++の黒魔術
C++の黒魔術C++の黒魔術
C++の黒魔術
 
プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法
 
What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?What's new in Spring Boot 2.6 ?
What's new in Spring Boot 2.6 ?
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?goで末尾再帰最適化は使えるか?
goで末尾再帰最適化は使えるか?
 
OpenStackで始めるクラウド環境構築入門
OpenStackで始めるクラウド環境構築入門OpenStackで始めるクラウド環境構築入門
OpenStackで始めるクラウド環境構築入門
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 
FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎
 
これから Haskell を書くにあたって
これから Haskell を書くにあたってこれから Haskell を書くにあたって
これから Haskell を書くにあたって
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
実践 NestJS
実践 NestJS実践 NestJS
実践 NestJS
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
OpenFOAMの壁関数
OpenFOAMの壁関数OpenFOAMの壁関数
OpenFOAMの壁関数
 
ブレソルでテラバイト級データのALTERを短時間で終わらせる
ブレソルでテラバイト級データのALTERを短時間で終わらせるブレソルでテラバイト級データのALTERを短時間で終わらせる
ブレソルでテラバイト級データのALTERを短時間で終わらせる
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理
 

Ähnlich wie Ekmett勉強会発表資料

Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料時響 逢坂
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座bleis tift
 
すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-Hiromasa Ohashi
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~Nobuhisa Koizumi
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミングOuka Yuka
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターンMoriharu Ohzu
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話bleis tift
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPIDaisuke Igarashi
 
すごいH 第12章モノイド
すごいH 第12章モノイドすごいH 第12章モノイド
すごいH 第12章モノイドShinta Hatatani
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPAkira Takahashi
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
プログラミング言語Scala
プログラミング言語Scalaプログラミング言語Scala
プログラミング言語ScalaTanUkkii
 

Ähnlich wie Ekmett勉強会発表資料 (20)

Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
 
Applicative functor
Applicative functorApplicative functor
Applicative functor
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座
 
すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
 
Haskell超入門 Part.1
Haskell超入門 Part.1Haskell超入門 Part.1
Haskell超入門 Part.1
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
Task
TaskTask
Task
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPI
 
すごいH 第12章モノイド
すごいH 第12章モノイドすごいH 第12章モノイド
すごいH 第12章モノイド
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JP
 
Swiftおさらい
SwiftおさらいSwiftおさらい
Swiftおさらい
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
プログラミング言語Scala
プログラミング言語Scalaプログラミング言語Scala
プログラミング言語Scala
 
Map
MapMap
Map
 

Ekmett勉強会発表資料

  • 1. Lens で Haskell をもっと格好良く! for 2013/3/31 ekmett 勉強会 ちゅーん
  • 2. 私はだあれ?  山手圏内で活動している 下っ端プログラマ  仕事の疲れは Haskell で癒す 日曜 Haskeller  Free モナドとか好きです  あと SDVX とか好き。音ゲーマーは Join me!
  • 3. 本日のメニュー  Lens とは何か  Lens でタプルを便利にする  任意のデータ型を Lens で使う  Lens の仕組ってどーなってんの?  Lens の便利な関数紹介  まとめ的な何か ※ ゆるふわ注意
  • 5. Lens とは・・・ タプルを始めとした任意のデータ構造の要素に対 する Setter や Getter を得るためのライブラリ Haskell で、 Java や C# といったオブジェクト指向 手続きプログラミングに似た記法で、要素にアクセ スできるようになる
  • 7. タプルの要素を取り出す方法  パターンマッチで取得する f (a, _, _) = a * 2  関数を定義して使う first (x, _, _) = x secound (_, x, _) = x third (_, _, x) = x g v = (first v) * 2
  • 8. ネストした内側の要素を取り出す  パターンマッチするとなんかキモい 全パターン網羅するとか無理ぽ f ((_, (_, _, x)), _, _) = x  関数合成を使えば綺麗&簡単 third.snd.first $ ((1, (1, 1, 999)), 1, 1) -- => 999
  • 9. 任意の位置の値を置き換えるには  パターンマッチを使って関数を書く。めんどい secondTo999 (x, _, y) = (x, 999, y)  関数を定義する setFirst x (_, a, b) = (x, a, b) setSecond x (a, _, b) = (a, x, b) setThird x (a, b, _) = (a, b, x) f'' = setSecond 999 (1, 2, 3) -- => (1,999,3)
  • 10. ネストした内側の値を変更  素直に関数定義・・・超キモい f x ((a, (b, c, _)), d, e) = ((a, (b, c, x)), d, e)  関数合成ではできない ghci> :t setFirst.setThird setFirst.setThird :: a -> (t, t2, t3) -> ((t4, t5, t1) -> (t4, t5, a), t2, t3)  こうすればちょっとはマシ f x t = setThird (setFirst x $ third t) t
  • 11. と に か く 超 不 満  値の取得と変更の識別子が異なる  タプルの要素数が異なると同じ識別子が使えない  構造がネストすると値の設定が超めんどい Java とか C# のような手続き言語で public 変数にアクセスするみたいに もっとスマートに構造を扱う事はできないの?
  • 12. と に か く 超 不 満  値の取得と変更の識別子が異なる  タプルの要素数が異なると同じ識別子が使えない  構造がネストすると値の設定が超めんどい そこで Lens ですよ!! Java とか C# のような手続き言語で public 変数にアクセスするみたいに もっとスマートに構造を扱う事はできないの?
  • 13. Lens のインストール  Cabal でいっぱつ $ cabal install lens  Hackage から直接アーカイブを取得 http://hackage.haskell.org/package/lens-3.9.0.2 コンパイルに時間がかかるので、 すごい H 本か TaPL あたりを読んでゆっくり待とう
  • 14. Lens を import  Haskell のソースコードに Import Control.Lens  あるいは、 ghci で :m Contorol.Lens
  • 15. Lens で要素の取得  (^.) と _1, _2, _3,... で簡単に取り出し ("Foo", "Bar", "Buz")^._1 -- => "Foo" ("Foo", "Bar", "Buz")^._2 -- => "Bar" ("Foo", "Bar", "Buz")^._3 -- => "Buz"  _1 ~ _9 まで別々の型クラスに定義されているので 要素数が異なるタプルに対しても同じように使える ('A', 'B', 'C', 'D', 'E')^._3 -- => 'C'
  • 16. ネストしたタプルから要素を取り出す  _1 ~ _9 は (.) で関数合成しても同じ型 ghci> :t _1 _1 :: (Functor f, Field1 s t a b, Indexable Int p) => p a (f b) -> s -> f t ghci> :t _1._2 _1._2 :: (Functor f, Field2 s1 t1 a b, Field1 s t s1 t1, Indexable Int p) => p a (f b) -> s -> f t  _1 ~ _9 を (.) で合成して、ネストした 複雑なタプルの内側の値をピンポイントで取り出し (100, 200, (310, (321, 322, 323, 999, 325), 330), 400)^._3._2._4                                           -- => 999
  • 17. Lens でタプルの値を変更  (.~) に _1 ~ _9 と、任意の値を適用 ghci> :t _2.~"Foo" _2.~"Foo" :: Field2 s t a [Char] => s -> t  要素が2つ以上のタプルは Field2 型クラス s のインスタンス ghci> :i Field2 class Field2 s t a b | s -> a, t -> b, s b -> t, t a -> s where _2 :: (Indexable Int p, Functor f) => p a (f b) -> s -> f t -- Defined in `Control.Lens.Tuple' ... -- Defined in `Control.Lens.Tuple' instance Field2 (a, b, c) (a, b', c) b b' -- Defined in `Control.Lens.Tuple' instance Field2 (a, b) (a, b') b b' -- Defined in `Control.Lens.Tuple'
  • 18. Lens でタプルの値を変更  (.~) に _1 ~ _9 と、任意の値を適用 ghci> :t _2.~"Foo" _2.~"Foo" :: Field2 s t a [Char] => s -> t  _2.~”Foo” にタプルを適用すると 二つ目の要素が ” Foo” に変更される _2.~"Foo" $ (1, 2) -- => (1,"Foo") _2.~"Foo" $ (1, 2, 3) -- => (1,"Foo",3) _2.~"Foo" $ (1, 2, 3, 4) -- => (1,"Foo",3,4)
  • 19. Lens でタプルの値を変更  勿論、 _1 ~ _9 を関数合成しても良い _4._2.~999 $ (1,2,3,(1,2,3),5) -- => (1,2,3,(1,999,3),5)  ($) の代わりに flip ($) と外延的等価な (&) を使う ghci> :i (&) (&) :: a -> (a -> b) -> b     -- Defined in `Control.Lens.Combinators' infixl 1 & (1,2,3,(1,2,3),5)&_4._2 .~ 999 -- => (1,2,3,(1,999,3),5)
  • 20. Lens でタプルの値を変更  勿論、 _1 ~ _9 を関数合成しても良い _4._2.~999 $ (1,2,3,(1,2,3),5) -- => (1,2,3,(1,999,3),5)  ($) の代わりに flip ($) と外延的等価な (&) を使う Java や C# の代入文そっくり! ghci> :i (&) (&) :: a -> (a -> b) -> b     -- Defined in `Control.Lens.Combinators' infixl 1 & (1,2,3,(1,2,3),5)&_4._2 .~ 999 -- => (1,2,3,(1,999,3),5)
  • 21. ここまでのまとめ タプルの操作には不満がまんまん でも Lens を使えば・・・  値の取得も変更も同じ識別子で参照できる  ネストしたタプルの値の変更も 手続き言語の代入感覚でらくらく書ける  それでいてしっかり型安全 ( これ重要) タプル以外の型もこんな風にできない?→
  • 23. Point 型 /Line 型を作る  次のような型を作る data Point = Point { x :: Int, y :: Int } deriving (Show, Eq) data Line = Line { startPoint :: Point, endPoint :: Point } deriving (Show, Eq)  次の値を例に色々考えてみよう sampleLine = Line { startPoint = Point { x = 100, y = 150 }, endPoint = Point { x = 200, y = 250 } }
  • 24. Point 単位の操作は簡単  取得 startPoint sampleLine -- => Point {x = 100, y = 150} endPoint sampleLine -- => Point {x = 200, y = 250}  置き換え SampleLine {    endPoint = Point { x = 1000, y = 1500 }}
  • 25. では、座標単位の操作は?  取得は関数合成を使えば良い x . endPoint $ sampleLine -- => 200  置き換えは・・・いまいち分りづらい sampleLine { endPoint = (endPoint sampleLine) { x = 999 } } -- => Line { startPoint = Point {x = 100, y = 150}, endPoint = Point {x = 999, y = 250}}
  • 26. では、座標単位の操作は?  取得は関数合成を使えば良い x . endPoint $ sampleLine -- => 200  置き換えは・・・いまいち分りづらい よし、 Lens を使おう! sampleLine { endPoint = (endPoint sampleLine) { x = 999 } } -- => Line { startPoint = Point {x = 100, y = 150}, endPoint = Point {x = 999, y = 250}}
  • 27. Point 型 /Line 型を Lens にする  フィールド名の前に” _” を付加し 『 makeLenses '' 型名』 と記述する data Point = Point { _x :: Int, _y :: Int } deriving (Show, Eq) makeLenses ''Point data Line = Line { _startPoint :: Point, _endPoint :: Point } deriving (Show, Eq) makeLenses ''Line ※ コンパイルのためには GHC 拡張の TemplateHaskell を  有効にしておく必要がある
  • 28. Point 型 /Line 型を Lens にする  フィールド名から” _” を抜いた識別子を使って (^.) や (.~) で要素にアクセスできるようになる sampleLine^.startPoint -- => Point {_x = 100, _y = 150} sampleLine^.endPoint -- => Point {_x = 200, _y = 250} sampleLine^.startPoint.x -- => 100 sampleLine^.endPoint.y -- => 250 sampleLine&startPoint.x.~999 -- => Line { -- _startPoint = Point {_x = 999, _y = 150}, -- _endPoint = Point {_x = 200, _y = 250}} sampleLine&endPoint.x.~999 -- => Line { -- _startPoint = Point {_x = 100, _y = 150}, -- _endPoint = Point {_x = 999, _y = 250}} カッコイイ!!
  • 29. こんな場合はどうなる?  型変数が含まれる型でも data Foo a = Foo { _hoge :: a, _piyo :: Int } deriving (Show, Eq) makeLenses ''Foo sampleFoo = Foo { _hoge = "Hello!", _piyo = 100 }  もちろん大丈夫☆(ゝ ω ・) v sampleFoo^.hoge -- => "Hello!" sampleFoo^.piyo -- => 100 sampleFoo&hoge.~True -- => Foo {_hoge = True, _piyo = 100} sampleFoo&piyo.~999 -- => Foo {_hoge = "Hello!", _piyo = 999}
  • 30. ここまでのまとめ 自分で作った型も Lens で操作したい! でも型とかややこしそうだし面倒では?  TemplateHaskell の力を借りて ちょ〜簡単に Lens になるよ  型変数を含む場合も無問題!  それでいてしっかり型安全 ( 大事なことなのでn (ry いったいどういう仕組みなんだろう?→
  • 32. Setter を作ろう  単純に 2 要素のタプルの 2 つめの要素を任意の 値に置き換える関数を考えると、次のような型にな る f :: a -> (b, c) -> (b, a)  これだけではつまらないので、一つ目の引数を関 数で取るようにする f :: (a -> b) -> (c, a) -> (c, b)
  • 33. Setter を作ろう  単純に 2 要素のタプルの 2 つめの要素を任意の 値に置き換える関数を考えると、次のような型にな る f :: a -> (b, c) -> (b, a) 値を x に置き換えたい場合は  const x を適用すれば良い これだけではつまらないので、一つ目の引数を関 数で取るようにする f :: (a -> b) -> (c, a) -> (c, b)
  • 34. ところで  この型、何かと似てない? f :: (a -> b) -> (c, a) -> (c, b) fmap :: Functor f => (a -> b) -> f a -> f b とそっくり・・・
  • 35. ところで  この型、何かと似てない? f :: (a -> b) -> (c, a) -> (c, b) ※ 衆知のとおり、 2 値のタプルは Functor になっ ていて、次のような事ができる fmap (*2) ("Hey!", 5) -- => ("Hey!",10) しかし Functor では一つ目の要素は操作できない さて、どうしよう?
  • 36. fmap のもうひとつの実装  Data.Traversable で定義されている Traversable 型クラスで、次の型を持つ traverse 関数が定義さ れている traverse :: Applicative f => (a -> f b) -> t a -> f (t b)  同モジュールの fmapDefault 関数は、 traverse 関 数を用いた fmap の別実装 fmapDefault :: Traversable t => (a -> b) -> t a -> t b fmapDefault f = getId . traverse (Id . f)
  • 37. fmap のもうひとつの実装  Data.Traversable で定義されている Traversable 型クラスで、次の型を持つ traverse 関数が定義さ れているData.Functor.Identity の定義に同じ Id は newtype Id a = Id { getId :: a } traverse :: Applicative f => (a -> f b) -> t a -> f (t b) Functor と Applicative のインスンタンス  同モジュールの fmapDefault 関数は、 traverse 関 数を用いた fmap の別実装 fmapDefault :: Traversable t => (a -> b) -> t a -> t b fmapDefault f = getId . traverse (Id . f)
  • 38. fmapDefault の 動作を決めるのは traverse 関数  なら、 traverse を別の関数と差し替えれば別の動 きをするんじゃなイカ?というわけで ... fmapDefault から traverse を外出しした over 関数 を定義すると、次のような型になる over :: ((a1 -> Id b) -> a -> Id c) -> (a1 -> b) -> a -> c over l f = getId . l (Id . f)  ここで、第一引数の型に対し Setter という別名を 付けよう type Setter s t a b = (a -> Id b) -> s -> Id t
  • 39. fmapDefault の 動作を決めるのは traverse 関数  なら、 traverse を別の関数と差し替えれば別の動 きをするんじゃなイカ?というわけで ... fmapDefault から traverse を外出しした over 関数 を定義すると、次のような型になる over :: Setter a c a1 b -> (a1 -> b) -> a -> c over l f = getId . l (Id . f)  ここで、第一引数の型に対し Setter という別名を 付けよう type Setter s t a b = (a -> Id b) -> s -> Id t
  • 40. fmapDefault の 動作を決めるのは traverse 関数  なら、 traverse を別の関数と差し替えれば別の動 きをするんじゃなイカ?というわけで ... これにより、 over の型がこう書ける fmapDefault から traverse を外出しした over 関数 を定義すると、次のような型になる over :: Setter a c a1 b -> (a1 -> b) -> a -> c over l f = getId . l (Id . f)  ここで、第一引数の型に対し Setter という別名を 付けよう type Setter s t a b = (a -> Id b) -> s -> Id t
  • 41. fmapDefault の 動作を決めるのは traverse 関数  なら、 traverse を別の関数と差し替えれば別の動 きをするんじゃなイカ?というわけで ... fmapDefault から traverse を外出しした over 関数 を定義すると、次のような型になる over :: Setter a c a1 b -> (a1 -> b) -> a -> c over l f = getId . l (Id . f)  ここで、第一引数の型に対し Setter という別名を 付けよう type Setter s t a b = (a -> Id b) -> s -> Id t
  • 42. fmapDefault の 動作を決めるのは traverse 関数  なら、 traverse を別の関数と差し替えれば別の動 きをするんじゃなイカ?というわけで ... fmapDefault から traverse を外出しした over 関数 を定義すると、次のような型になる over :: Setter a c a1 b -> (a1 -> b) -> a -> c over l f = getId . l (Id . f)  ここで、第一引数の型に対し Setter という別名を 当然、 traverse 関数を適用すれば 付けよう fmapDefault と同値になる さらに何かしら Setter 型の関数を引数に取る事により type Setter s t a b = (a -> Id b) -> s -> Id t fmap と似た別の関数を得る事ができる
  • 43. _1, _2 を実装するには?  最終的に欲しい型 Over _1 :: (a -> b) -> (a, v) -> (b, v)  over の型を読み替え Over :: Setter (a, v) (b, v) a b -> (a -> b) -> (a, v) -> (b, v)  _1 の型 _1 :: Setter (a, v) (b, v) a b -- つまり _1 :: (a -> Id b) -> (a, v) -> Id (b, v)
  • 44. 実際にやってみる  導きだした型を満足させるよう _1, _2 を実装 _1 :: Setter (a, v) (b, v) a b _1 f (x, y) = Id (getId . f $ x, y) _2 :: Setter (v, a) (v, b) a b _2 f (x, y) = Id (x, getId . f $ y)  任意の要素に fmap できるようになる! (over _1) (*2) (50, 50) -- => (100,50) (over _2) (*2) (50, 50) -- => (50,100)
  • 45. こうなれば後は簡単  (.~) は次のようにして簡単に再実装できる (.~) :: Setter s t a b -> b -> s -> t a .~ v = over a (const v)  Lens と同じ書き方で要素を変更できるようになる _1.~999 $ (1, 2) => (999,2) _2.~999 $ (1, 2) => (1,999)
  • 46. それじゃぁ次は Getter だ!  2 値のタプルからの値の取得は次のような型をイ メージできる f :: (a, b) -> b  単に取り出すだけでなく、何か関数を適用して返す ようにしてみると・・・ f :: (a -> b) -> (c, a) -> b  これは Data.Foldable で定義されている foldMap 関数とそっくり foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
  • 47. Traversable の foldMapDefault  FoldMapDefault の定義が Data.Traversable に! foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> t a -> m foldMapDefault f = getConst . traverse (Const . f) Const は Data.Functor.Constant に定義 newtype Const a b = Const {getConst :: a} Foldable や Applicative 等のインスンタンス
  • 48. 同じようにして traverse を外に出す  foldMapDefault の実装から traverse を取り出し foldMapOf 関数を定義 foldMapOf :: ((a1 -> Const b1 b2) -> a -> Const c b) -> (a1 -> b1) -> a -> c foldMapOf l f = getConst . l (Const . f)  第一引数の関数の型に別名を付けてみる type Getting r s a = (a -> Const r a) -> s -> Const r s
  • 49. アクセサの定義、 foldMapOf への適用  改めて、 2 値のタプルに対する _1, _2 を次のよう に定義 _1 :: Getting r (a, s) a _1 f (x, _) = Const (getConst . f $ x) _2 :: Getting r (s, a) a _2 f (_, y) = Const (getConst . f $ y)  foldMapOf と組み合わせて任意の場所の要素を foldMap (foldMapOf _1) (*2) (100, 1000) -- => 200 (foldMapOf _2) (*2) (100, 1000) -- => 2000
  • 50. (^.) の実装も超簡単 (^.) :: s -> Getting a s a -> a v ^. l = (foldMapOf l) id v 値をそのまま取り出したいのだから 引数に対して何もしない関数 id :: a -> a を、適用してやれば良い (111, 222)^._1 -- => 111 (111, 222)^._2 -- => 222
  • 51. Setter と Getting  どちらも traverse 関数を元に定義された型なのだ から、揃える事はできないだろうか? type Getting r s a = (a -> Const r a) -> s -> Const r s type Setter s t a b = (a -> Id b) -> s -> Id t  Getting の型変数を Setter に合わせて変えてみる type Getting s t a b = forall m .Monoid m => (a -> Const m b) -> s -> Const m t type Setter s t a b = (a -> Id b) -> s -> Id t
  • 52. Setter と Getting  どちらも traverse 関数を元に定義された型なのだ から、揃える事はできないだろうか? type Getting r s a = 型定義に登場しない型変数 m (a -> Const r a) -> s -> Const r s type Setter s t a b = コンパイルのため、 FoldMap の実装に合せ (a -> Id b) -> s -> Id t Monoid を要求するようにしておく でもあまり嬉しくない制約  Getting の型変数を Setter に合わせて変えてみる type Getting s t a b = forall m .Monoid m => (a -> Const m b) -> s -> Const m t type Setter s t a b = (a -> Id b) -> s -> Id t
  • 53. Id も Const も Functor !  従って、次の赤字の部分は、 Functor を要求する 型変数に置き換えることができる type Getting s t a b = forall m .Monoid m => (a -> Const m b) -> s -> Const m t type Setter s t a b = (a -> Id b) -> s -> Id t  これで型宣言も一つに纏められる しかも Getter の Monoid も消えた!やったね! type Lens s t a b = forall f .Functor f => (a -> f b) -> s -> f t
  • 54. _1, _2 を作り替える  後は _1 と _2 を、それぞれ Lens 型に合うように実 装 _1 :: Lens (a, v) (b, v) a b _1 f (x, y) = fmap (x' -> (x', y)) (f x) _2 :: Lens (v, a) (v, b) a b _2 f (x, y) = fmap (y' -> (x, y')) (f y)  ちゃんと使えるかどうか確認・・・バッチリ! (100, 200)^._1 -- => 100 _1.~999 $ (100, 200) -- => (999,200)
  • 55. traverse. traverse  traverse 関数同士を関数合成するとこうなる traverse :: (Control.Applicative.Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) traverse.traverse :: (Control.Applicative.Applicative f, Traversable t, Traversable t1) => (a -> f b) -> t (t1 a) -> f (t (t1 b)) traverse.traverse.traverse :: (Control.Applicative.Applicative f, Traversable t, Traversable t1, Traversable t2) => (a -> f b) -> t (t1 (t2 a)) -> f (t (t1 (t2 b))) 合成しても性質が維持される!
  • 56. Lnes 型の関数は traverse と同じ型  なら _1 や _2 も同じ性質を持っているはず・・・ _2 :: Functor f => (a -> f b) -> (v, a) -> f (v, b) _2._2 :: Functor f => (a -> f b) -> (v, (v1, a)) -> f (v, (v1, b)) _2._2._2 :: Functor f => (a -> f b) -> (v, (v1, (v2, a))) -> f (v, (v1, (v2, b))) Lens が関数合成して使えるのは 型を見れば当然の事だった!!
  • 57. そんなワケで  今回再実装したオレオレ Lens も _1 や _2 を関数合成して、ネストしたタプルの好き な要素にアクセスできるよっ _2._1.~"Lens" $ ("Hello", ((), "World")) -- => ("Hello",("Lens","World")) ("Hello", ("Lens", "World"))^._2._1 -- => "Lens"
  • 58. (注)  今回の再実装で Id 、 Const という型を使ったが、 実際の Lens の実装では Mutator 、 Accessor と いう別実装を用いている これは、型エラーが発生した時に、よりエラーの原 因を特定しやすくするため。
  • 59. ここまでのまとめ Lens の仕組みって凄い複雑そう・・・ 超人的な知能を持っていないと理解できない んじゃ?  Traversable 型クラスの fmapDefault 関 数 /foldMapDefault 関数から、型を中心に 追っていけば自然と導き出せるよ! もっと Lens の事が知りたいな!→
  • 61. と、その前に・・・  Control.Lens モジュール内では、 Lens と同じよう な型に様々な別名が付けられていて・・・ ・ Lens ・ Getter ・ Setter ・ Fold ・ Action ... 等々 それぞれ型クラスの制約なんかが少しづつ違って いたりするので、必要に応じて Hackage を参照
  • 62. foldMapOf 関数 / over 関数  前の節で実装した foldMapOf 関数と over 関数は Lens モジュールをインポートしてそのまま使える (foldMapOf _2) (*100) (1, 2, 3) --=> 200 (over _2) (*100) (1, 2, 3) -- => (1,200,3) (foldMapOf y) (*2) $ Point { _x = 100, _y = 200 } -- => 400 (over x) (*2) $ Point { _x = 100, _y = 200 } -- => Point {_x = 200, _y = 200}  %~ は over の中置バージョン _2 %~ (*100) $ (1, 2, 3) -- => (1,200,3)
  • 63. to 関数で関数適用  to 関数を使えば、 (^.) で取得した値に対して関数 適用できる (1, 2, 3)^._2.to (*100) -- => 200  さらに関数合成を連ねて次のようにしても良い (1,(10,20),3)^._2.to swap._2.to (*100)                          -- => 1000
  • 64. Setter の演算子色々  対象となる要素が Num 型クラスのインスタンスや Bool 等の特定の型であれば、それらに対して便利 な演算子を使う事ができる。 -- 加算 (10, 20)&_1 +~ 100 -- => (110,20) -- 減算 (10, 20)&_1 -~ 5 -- => (5,20) -- 乗算 (10, 20)&_1 *~ 100 -- => (1000,20) -- 除算 (10, 20)&_1 //~ 5 -- => (2.0,20) --AND (True, 1)&_1 &&~ False -- => (False,1) --OR (True, 1)&_1 ||~ False -- => (True,1)
  • 65. (.=) と use 関数  どちらも型クラス制約に MonadState クラスが含ま れている。 状態系のモナドと組み合わせて使う関数。 ghci> :t (.=) (.=) :: MonadState s m => ASetter s s a b -> b -> m () ghci> :t use use :: MonadState s m => Getting a s t a b -> m a
  • 66. (.=) と use 関数  (.=) や use の簡単な例: sample :: State Line () sample = do -- (.=) で状態に代入 startPoint .= Point { _x = 100, _y = 200 } endPoint.x .= 300 -- 状態から値を取り出し sp <- use startPoint epx <- use $ endPoint.x return ()
  • 67. 各 Setter 演算子の MonadState バージョン  何処かの言語で見たような書き方ができる sample2 = do v %= (*100) --over v += 10 -- 加算 v -= 10 -- 減算 v *= 10 -- 乗算 v //= 10 -- 除算 b ||= True --OR b &&= True --AND ※ 「状態」に対してかなりやりたい放題できるように なるので乱用注意!
  • 68. Getter のアクセス時に モナドアクションを付加する Action  (^.) の代わりに (^!) を使うと、 act 関数を使ってモ ナドのアクセサにモナドアクションを追加する事が できる (("Foo", "Bar"), "Buz")^!_1.act (Just)._2 -- => Just "Bar" (("Lens", "Hello"), "World!")^!_1.act (x -> print x >> return x).to swap -- => ("Hello","Lens") ※ 途中で ("Lens","Hello") を print  次は Nothing になるかと思ったけど、 No instance エラーになった・・・ (´ ・ ω ・ `) ? (("Foo", "Bar"), "Buz")^!_1.act (const Nothing)._2
  • 69. などなど  Lens モジュールには Lens をより便利に使う仕組 みが沢山用意されているので、 Hackage を一読し てみよう! http://hackage.haskell.org/package/lens-3.9.0.2
  • 70. ところで・・・これは何だ・・・?  Lens の構成図を見る と、さらに下層に ISO とか Equality とか ゆー輩がいます…が Lens ISO Equality 勉強不足でご紹介で きないです orz ゴメンナサイ
  • 72. 改めて Lens って何?  レンズで覗くように複雑なデータ構造の内部に自在 にアクセスする手段を提供してくれるライブラリ軍  仕組みはやや複雑だけど Traversable を起点に 考えていけば考え方はとっても明瞭  単なるアクセサの域を超えた自在な操作を可能に するスーパーウルトラアルティメットアクセサ  もうちょっと秘められたパワーがありそうです
  • 74.
  • 76. (((( ;゚ Д ゚ )))) こ・・・怖くねーし Lens 全然怖くねーから・・・
  • 77. さぁ、みんな Lens を使おう! ご清聴ありがとうございました!!