SlideShare uma empresa Scribd logo
1 de 68
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
と に か く 超 不 満

   値の取得と変更の識別子が異なる
   タプルの要素数が異なると同じ識別子が使えな
    い
   構造がネストすると値の設定が超めんどい
      そこで Lens ですよ!!
    Java とか C# のような手続き言語で
    public 変数にアクセスするみたいに
    もっとスマートに構造を扱う事はできないの?
Lens のインストール
   Cabal でいっぱつ

$ cabal install lens


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


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


    コンパイルに時間がかかるので、
    すごい 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 を (.) で合成して、ネストした
         複雑なタプルの内側の値をピンポイントで取り
         出し
, (310, (321, 322, 323, 999, 325), 330), 400)^._3._2._4
                                     
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 ($) と外延的等価な (&) を使
     う 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

     置き換えは・・・いまいち分りづらい
           よし、 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)
         値を 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 型クラスで、次の型を持つ
         Id は 関数が定義されている
     traverseData.Functor.Identity の定義に同じ
                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 を別の関数と差し替えれば別の
     動きをするんじゃなイカ?というわけで ...
           これにより、 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 という別名
         当然、 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 =
                     型定義に登場しない型変数 m
            (a -> Const r a) -> s -> Const r s
    type Setter s t a b =
            コンパイルのため、 FoldMap の実装に合せ
            (a ->Monoid を要求するようにしておく
                  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
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
      える (*100) (1, 2, 3) -- => (1,200,3)
    (over _2)

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

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



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

     to 関数を使えば、 (^.) で取得した値に対して関
      数適用できる
      (1, 2, 3)^._2.to (*100) -- => 200

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

(10,20),3)^._2.to swap._2.to (*100)
                        -- =>
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 を使おう!




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

Mais conteúdo relacionado

Mais procurados

すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-Hiromasa Ohashi
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜勝成 鈴江
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるHideyuki Tanaka
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~Nobuhisa Koizumi
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Prunus 1350
 
これからの「言語」の話をしよう ―― 未来を生きるためのツール
これからの「言語」の話をしよう ―― 未来を生きるためのツールこれからの「言語」の話をしよう ―― 未来を生きるためのツール
これからの「言語」の話をしよう ―― 未来を生きるためのツールNobuhisa Koizumi
 
C++でHello worldを書いてみた
C++でHello worldを書いてみたC++でHello worldを書いてみた
C++でHello worldを書いてみたfirewood
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論MITSUNARI Shigeo
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会Akihiko Matuura
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swiftnecocen
 
Feature Selection with R / in JP
Feature Selection with R / in JPFeature Selection with R / in JP
Feature Selection with R / in JPSercan Ahi
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competitionyak1ex
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCamlHaruka Oikawa
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数Shinichi Kozake
 

Mais procurados (20)

すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-すごいHaskell楽しく学ぼう-第12章モノイド-
すごいHaskell楽しく学ぼう-第12章モノイド-
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
Map
MapMap
Map
 
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章
 
これからの「言語」の話をしよう ―― 未来を生きるためのツール
これからの「言語」の話をしよう ―― 未来を生きるためのツールこれからの「言語」の話をしよう ―― 未来を生きるためのツール
これからの「言語」の話をしよう ―― 未来を生きるためのツール
 
C++でHello worldを書いてみた
C++でHello worldを書いてみたC++でHello worldを書いてみた
C++でHello worldを書いてみた
 
初めてのSTL
初めてのSTL初めてのSTL
初めてのSTL
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
CLR/H No.35-2
CLR/H No.35-2CLR/H No.35-2
CLR/H No.35-2
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift
 
Feature Selection with R / in JP
Feature Selection with R / in JPFeature Selection with R / in JP
Feature Selection with R / in JP
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数
 

Semelhante a Ekmett勉強会発表資料

モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座bleis tift
 
(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
 
プログラミング言語Scala
プログラミング言語Scalaプログラミング言語Scala
プログラミング言語ScalaTanUkkii
 
Chainerの使い方と自然言語処理への応用
Chainerの使い方と自然言語処理への応用Chainerの使い方と自然言語処理への応用
Chainerの使い方と自然言語処理への応用Seiya Tokui
 
命令プログラミングから関数プログラミングへ
命令プログラミングから関数プログラミングへ命令プログラミングから関数プログラミングへ
命令プログラミングから関数プログラミングへNaoki Kitora
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜Hiromi Ishii
 
Implicit Explicit Scala
Implicit Explicit ScalaImplicit Explicit Scala
Implicit Explicit ScalaKota Mizushima
 
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
 
Ⅰ. Rの基礎 2017
Ⅰ. Rの基礎 2017Ⅰ. Rの基礎 2017
Ⅰ. Rの基礎 2017wada, kazumi
 
言語処理系入門€5
言語処理系入門€5言語処理系入門€5
言語処理系入門€5Kenta Hattori
 
実務者のためのかんたんScalaz
実務者のためのかんたんScalaz実務者のためのかんたんScalaz
実務者のためのかんたんScalazTomoharu ASAMI
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPIDaisuke Igarashi
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Springanyakichi
 

Semelhante a Ekmett勉強会発表資料 (20)

モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座
 
(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
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
 
プログラミング言語Scala
プログラミング言語Scalaプログラミング言語Scala
プログラミング言語Scala
 
Pythonintro
PythonintroPythonintro
Pythonintro
 
Chainerの使い方と自然言語処理への応用
Chainerの使い方と自然言語処理への応用Chainerの使い方と自然言語処理への応用
Chainerの使い方と自然言語処理への応用
 
命令プログラミングから関数プログラミングへ
命令プログラミングから関数プログラミングへ命令プログラミングから関数プログラミングへ
命令プログラミングから関数プログラミングへ
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
 
R intro
R introR intro
R intro
 
Implicit Explicit Scala
Implicit Explicit ScalaImplicit Explicit Scala
Implicit Explicit Scala
 
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
 
Ⅰ. Rの基礎 2017
Ⅰ. Rの基礎 2017Ⅰ. Rの基礎 2017
Ⅰ. Rの基礎 2017
 
言語処理系入門€5
言語処理系入門€5言語処理系入門€5
言語処理系入門€5
 
実務者のためのかんたんScalaz
実務者のためのかんたんScalaz実務者のためのかんたんScalaz
実務者のためのかんたんScalaz
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPI
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Spring
 

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. と に か く 超 不 満  値の取得と変更の識別子が異なる  タプルの要素数が異なると同じ識別子が使えな い  構造がネストすると値の設定が超めんどい そこで Lens ですよ!! Java とか C# のような手続き言語で public 変数にアクセスするみたいに もっとスマートに構造を扱う事はできないの?
  • 12. Lens のインストール  Cabal でいっぱつ $ cabal install lens  Hackage から直接アーカイブを取得 http://hackage.haskell.org/package/lens-3.8.7.3 コンパイルに時間がかかるので、 すごい H 本か TaPL あたりを読んでゆっくり待 とう
  • 13. Lens を import  Haskell のソースコードに Import Control.Lens  あるいは、 ghci で :m Contorol.Lens
  • 14. 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' える
  • 15. ネストしたタプルから要素を取り出 す  _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 を (.) で合成して、ネストした 複雑なタプルの内側の値をピンポイントで取り 出し , (310, (321, 322, 323, 999, 325), 330), 400)^._3._2._4                                      
  • 16. 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'
  • 17. 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)
  • 18. 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)
  • 19. ここまでのまとめ タプルの操作には不満がまんまん でも Lens を使えば・・・  値の取得も変更も同じ識別子で参照できる  ネストしたタプルの値の変更も 手続き言語の代入感覚でらくらく書ける  それでいてしっかり型安全 ( これ重要) タプル以外の型もこんな風にできない?→
  • 21. 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 } }
  • 22. Point 単位の操作は簡単  取得 startPoint sampleLine -- => Point {x = 100, y = 150} endPoint sampleLine -- => Point {x = 200, y = 250}  置き換え SampleLine {    endPoint = Point { x = 1000, y = 1500 }}
  • 23. では、座標単位の操作は?  取得は関数合成を使えば良い x . endPoint $ sampleLine -- => 200 置き換えは・・・いまいち分りづらい よし、 Lens を使おう!  sampleLine { endPoint = (endPoint sampleLine) { x = 999 } } -- => Line { startPoint = Point {x = 100, y = 150}, endPoint = Point {x = 999, y = 250}}
  • 24. 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 を  有効にしておく必要がある
  • 25. 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}} カッコイイ!!
  • 26. こんな場合はどうなる?  型変数が含まれる型でも 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}
  • 27. ここまでのまとめ 自分で作った方も Lens で操作したい! でも型とかややこしそうだし面倒では?  TemplateHaskell の力を借りて ちょ〜簡単に Lens になるよ  型変数を含む場合も無問題!  それでいてしっかり型安全 ( 大事なことなのでn (ry いったいどういう仕組みなんだろう?→
  • 29. Setter を作ろう  単純に 2 要素のタプルの 2 つめの要素を任意の 値に置き換える関数を考えると、次のような型 になる f :: a -> (b, c) -> (b, a) 値を x に置き換えたい場合は  const x を適用すれば良い これだけではつまらないので、一つ目の引数を 関数で取るようにする f :: (a -> b) -> (c, a) -> (c, b)
  • 30. ところで  この型、何かと似てない? f :: (a -> b) -> (c, a) -> (c, b) fmap :: Functor f => (a -> b) -> f a -> f b とそっくり・・・
  • 31. ところで  この型、何かと似てない? f :: (a -> b) -> (c, a) -> (c, b) ※ 衆知のとおり、 2 値のタプルは Functor に なっていて、次のような事ができる fmap (*2) ("Hey!", 5) -- => ("Hey!",10) しかし Functor では一つ目の要素は操作できな い
  • 32. fmap のもうひとつの実装  Data.Traversable で定義されている Traversable 型クラスで、次の型を持つ Id は 関数が定義されている traverseData.Functor.Identity の定義に同じ 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)
  • 33. 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
  • 34. 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
  • 35. 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 と似た別の関数を得る事ができる
  • 36. _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)
  • 37. 実際にやってみる  導きだした型を満足させるよう _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)
  • 38. こうなれば後は簡単  (.~) は次のようにして簡単に再実装できる (.~) :: 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)
  • 39. それじゃぁ次は 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
  • 40. 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 等のインスンタンス
  • 41. 同じようにして 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
  • 42. アクセサの定義、 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
  • 43. (^.) の実装も超簡単 (^.) :: s -> Getting a s a -> a v ^. l = (foldMapOf l) id v 値をそのまま取り出したいのだから 引数に対して何もしない関数 id :: a -> a を、適用してやれば良い (111, 222)^._1 -- => 111 (111, 222)^._2 -- => 222
  • 44. 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 ->Monoid を要求するようにしておく 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
  • 45. 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
  • 46. _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)
  • 47. 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))) 合成しても性質が維持される!
  • 48. 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 が関数合成して使えるのは 型を見れば当然の事だった!!
  • 49. そんなワケで  今回再実装したオレオレ Lens も _1 や _2 を関数合成して、ネストしたタプルの 好きな要素にアクセスできるよっ _2._1.~"Lens" $ ("Hello", ((), "World")) -- => ("Hello",("Lens","World")) ("Hello", ("Lens", "World"))^._2._1 -- => "Lens"
  • 50. (注)  今回の再実装で Id 、 Const という型を使った が、 実際の Lens の実装では Mutator 、 Accessor と いう別実装を用いている これは、型エラーが発生した時に、よりエラー の原因を特定しやすくするため。
  • 51. ここまでのまとめ Lens の仕組みって凄い複雑そう・・・ 超人的な知能を持っていないと理解でき ないんじゃ?  Traversable 型クラスの fmapDefault 関 数 /foldMapDefault 関数から、型を中心に 追っていけば自然と導き出せるよ! もっと Lens の事が知りたいな!→
  • 53. と、その前に・・・  Control.Lens モジュール内では、 Lens と同じ ような型に様々な別名が付けられていて・・・ ・ Lens ・ Getter ・ Setter ・ Fold ・ Action ... 等々 それぞれ型クラスの制約なんかが少しづつ違っ ていたりするので、必要に応じて Hackage を
  • 54. foldMapOf 関数 / over 関数  前の節で実装した foldMapOf 関数と over 関数 は Lens モジュールをインポートしてそのまま使 (foldMapOf _2) (*100) (1, 2, 3) --=> 200 える (*100) (1, 2, 3) -- => (1,200,3) (over _2) (foldMapOf y) (*2) $ Point { _x = 100, _y = 200 } -- => 400 (over x) (*2) $ Point { _x = 100, _y = 200 } -- => Point {_x = 200, _y = 200} _2 %~ (*100) $ (1, 2, 3) -- => (1,200,3)  %~ は over の中置バージョン
  • 55. to 関数で関数適用  to 関数を使えば、 (^.) で取得した値に対して関 数適用できる (1, 2, 3)^._2.to (*100) -- => 200  さらに関数合成を連ねて次のようにしても良い (10,20),3)^._2.to swap._2.to (*100)                         -- =>
  • 56. 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)
  • 57. (.=) と 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
  • 58. (.=) と use 関数  (.=) や use の簡単な例: sample :: State Line () sample = do -- (.=) で状態に代入 startPoint .= Point { _x = 100, _y = 200 } endPoint.x .= 300 -- 状態から値を取り出し sp <- use startPoint epx <- use $ endPoint.x return ()
  • 59. 各 Setter 演算子の MonadState バージョン  何処かの言語で見たような書き方ができる sample2 = do v %= (*100) --over v += 10 -- 加算 v -= 10 -- 減算 v *= 10 -- 乗算 v //= 10 -- 除算 b ||= True --OR b &&= True --AND ※ 「状態」に対してかなりやりたい放題できる ようになるので乱用注意!
  • 60. 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
  • 61. などなど  Lens モジュールには Lens をより便利に使う仕 組みが沢山用意されているので、 Hackage を 一読してみよう! http://hackage.haskell.org/package/lens-3.9.0.2
  • 62. ところで・・・これは何だ・・・?  Lens の構成図を見 ると、さらに下層に ISO とか Equality と かゆー輩がいます… が Lens ISO Equality 勉強不足でご紹介で きないです orz
  • 64. 改めて Lens って何?  レンズで覗くように複雑なデータ構造の内部に 自在にアクセスする手段を提供してくれるライ ブラリ軍  仕組みはやや複雑だけど Traversable を起点に 考えていけば考え方はとっても明瞭  単なるアクセサの域を超えた自在な操作を可能 にするスーパーウルトラアルティメットアクセ サ  もうちょっと秘められたパワーがありそうです
  • 67. (((( ;゚ Д ゚ )))) こ・・・怖くねーし Lens 全然怖くねーから・・・
  • 68. さぁ、みんな Lens を使おう! ご清聴ありがとうございまし た!!