ElmでFocusとLensの比較

前にElmでLensというライブラリを触っていたところ、フォロワーさんからFocusで良いのではないか？との意見をもらったのを最近思い出して簡単に比較したので記事にしました。

-> [発展編へ](https://qiita.com/ababup1192/items/a874a87f33b26650f969)

## なぜFocus, Lens?

FocusとLensは複雑なレコードを操作(get, set, updateなど)するためのライブラリです。Elmを知らない方はなぜ、わざわざgetter, setterなどを別途ライブラリを持ち出さないといけないか謎だと思います。なぜかを知るために、まずはじめにElmのレコードについて見ていきましょう。Elmのレコードは一見すると、JavaScriptのオブジェクトのコロン(:)がイコール(=)に変わっただけのように見えます。

```haskell
> r1 = { x = 1, y = 2 }
{ x = 1, y = 2 } : { x : number, y : number1 }

> r1.x
1 : number
```

大きな違いはイミュータブル(不変)なデータ構造という点です。以下の例では、```r2```の```y```を2に変えることで```r1```と一致します。しかしこのとき```r2```は更新されているわけではなく、```r2```の```y```の値を2にした新しいレコードを生成しています。イミュータブルなデータ構造を用いることで、予期せぬ値の変更に怯えなくてよかったり、基本的にデータを比較する時に同値性だけを気にすれば良くなります(Javaでは```equals```を別途定義したり、JavaScriptでは文字列にして比較する必要があります)。

```haskell
> r2 = { x = 1, y = 5 }
{ x = 1, y = 5 } : { x : number, y : number1 }
> r1 == { r2 | y = 2 }
True : Bool
> r2
{ x = 1, y = 5 } : { x : number, y : number1 }
```

それでは、どのようなケースでFocusやLensが必要になってくるのでしょうか。それは以下のようにネストしたデータ構造の場合に必要になります。

```haskell
type alias Object =
    { position : Point
    , velocity : Point
    }


type alias Point =
    { x : Float
    , y : Float
    }
```

つまり標準では、以下のような操作が出来ません。

```haskell
> object = Object (Point 3 4) (Point 1 1)
> { object | position = { object.position | x = 5 } }
```

例えば、```object.position.x```を変更するには、以下のような関数が必要になります。古い```position```を変数に束縛し、新しい```position```を作り、```object```の```position```に新しい```position```を設定する。なんだか面倒ですね。

```haskell
setPositionX x object =
    let
        oldPosition =
            object.position

        newPosition =
            { oldPosition
                | x = x
            }
    in
        { object
            | position = newPosition
        }

setPositionX 5 object
```

## FocusとLensを比較してみよう

それでは、２つのライブラリの比較をしてみましょう。

### Focus

[Focus](http://package.elm-lang.org/packages/evancz/focus/latest/Focus)は、大きなデータ構造```big```の小さなデータ構造```small```に集中することができます。小さなデータ構造とは、レコードの特定のフィールドのことです。そうすることで大きなデータ構造に対する小さなデータ構造の取得、設定、更新が行なえます。

```haskell=
type Focus big small
```

![](https://i.imgur.com/Mzs16A7.png)


それではFocusの取得関数```get```について見ていきましょう。第一引数にFocus、第二引数に取得の対象となるデータ構造を渡します。結果としてFocusされたデータ(構造)を取得することができます。

```haskell
get : Focus big small -> big -> small
```

簡単なサンプルを見てみましょう。```x```は、レコードからフィールドxにフォーカスする関数です。レコード```{ x=3, y=4 }```の```x```にフォーカスし、取得を行います。

```haskell
x : Focus { record | x:a } a

get x { x=3, y=4 } == 3
```

今度は、設定関数```set```を見ていきましょう。基本は```get```と同じです。フォーカスして、設定したい値を渡すだけで完了です。

```haskell
set : Focus big small -> small -> big -> big

x : Focus { record | x:a } a

set x 42 { x=3, y=4 } == { x=42, y=4 }
```



### Lens

[Lens](http://package.elm-lang.org/packages/arturopala/elm-monocle/1.4.0/Monocle-Lens)は、Monocleというモジュール群にあるモジュールの一つです。Lensは、大きなデータ構造```a```の小さなデータ構造```b```に集中することができます。小さなデータ構造とは、レコードの特定のフィールドのことです。そうすることで大きなデータ構造に対する小さなデータ構造の取得、設定、更新が行なえます。

・・・あれ？ そうです。FocusとLensは型変数が違うだけで、定義が全く同じなのです。

```haskell
type alias Lens a b = 
    { get : a -> b
    , set : b -> a -> a
    }
```

実際に、Lensの定義を```a=big```、```b=small```と置き換えてFocusと並べてみるとまったく一致することがわかります。

```haskell
type alias Lens big small = 
    { get : big -> small
    , set : small -> big -> big
    }

type Focus big small
get : Focus big small -> big -> small
set : Focus big small -> small -> big -> big
```

### 使用例

具体的な使用例を見ていきましょう。以下のようなデータがあった場合に、dt(Float)だけ時間が経ったときのObjectを取得する関数```step```を定義してみましょう。

```haskell
type alias Point =
    { x : Float
    , y : Float
    }

type alias Object =
    { position : Point
    , velocity : Point
    }

Object (Point 3 4) (Point 1 1)

step : Float -> Object -> Object
```

まずは、FocusもLensも使用しない```step```関数です。

```haskell
step : Float -> Object -> Object
step dt object =
    let
        oldPosition =
            object.position

        newPosition =
            { oldPosition
                | x = oldPosition.x + object.velocity.x * dt
                , y = oldPosition.y + object.velocity.y * dt
            }
    in
        { object
            | position = newPosition
        }
```

それでは、FocusとLensを使った方法に切り替えていきましょう。まずは、各フィールドに対するFocus, Lensを定義していきましょう。

まずは愚直に定義した場合の、```x```のLensを見ていきましょう。Lensは、```get```と```set```関数を受け取ることで形成することができます。```get```は、```Point```型のレコードからxを取得する関数です。```set```は、新しいxとPoint型を受取、xを更新した新しいPoint型を生成する関数です。

```haskell
get : a -> b
set : b -> a -> a

x : Lens { r | x : Float } Float
x =
    let
        get point =
            point.x

        set x point =
            { point | x = x }
    in
        Lens get set
```

これは以下のように一般化、簡略化することができます。フィールド```x```は```Float```型である必要は無いため型変数```a```で抽象化します。```get```は、```.x```のようにフィールドを参照する関数で省略できます。```set```は無名関数で置き換えます。

```haskell
x : Lens { r | x : a } a
x =
    Lens .x (\x r -> { r | x = x })
```

Focusを作るには、```create```関数を利用することで生成することができます。Lensとほぼ同じですが、```set```ではなく更新関数```update``` ```((small -> small) -> big -> big)```を受け取るようです。

```haskell
create
    :  (big -> small)
    -> ((small -> small) -> big -> big)
    -> Focus big small

x : Focus { r | x : a } a
x =
    Focus.create .x (\f r -> { r | x = f r.x })
```

他の関数は、基本的にFocus, Lens関数```x```と同じなため省略します。step関数自体の実装を見ていきましょう。

Lensの場合は以下のような実装になります。Lensをcompose(=>)関数で合成し、目的のLensに到達します。この例では、```Object```から```Position.x```に焦点を当てるLensです。Lensが完成したら、modify関数に、```x```を更新する関数を渡します。今回は、```x```と```y```を変更する必要があるのでパイプで処理を続けていくだけで終りとなります。

```haskell
Lens.modify : Lens a b -> (b -> b) -> a -> a

step : Float -> Object -> Object
step dt object =
    object
        |> Lens.modify (position => x) (\px -> px + object.velocity.x * dt)
        |> Lens.modify (position => y) (\py -> py + object.velocity.y * dt)


(=>) : Lens a b -> Lens b c -> Lens a c
(=>) =
    compose
infixr 9 =>
```

![](https://i.imgur.com/OJtPhu8.png)


Focusの場合には、```update```を代わりに用います。Lensの```modify```と全く同じなため、```step```の実装も何と全く同じになってしまいます！

```haskell
update : Focus big small -> (small -> small) -> big -> big

step : Float -> Object -> Object
step dt object =
    object
        |> update (position => x) (\px -> px + object.velocity.x * dt)
        |> update (position => y) (\py -> py + object.velocity.y * dt)
```

簡単に試せるように[全体](https://github.com/ababup1192/elm-lens-focus-sample)を用意しておきました。お試しください。

## 結論

FocusとLens一体何が違ったのでしょうか？

- LensはMonocleの一部のモジュール
    - Common
    - Iso
    - Lens
    - Optional
    - Prism 
- FocusにないLensの汎用関数
    - ```modify2, 3```
    - ```modifyAndMerge```
    - ```zip```
    - ```tuple```
- Focusは生成時に```get```と```update```を必要とする
- Lensは生成時に```get```と```set```を必要とする

生成に関してはLensの方が簡単です。Focusの方は様々な関数を定義するときには使い勝手が良いと思われます。しかし個人的には、LensはFocusと比べて多くの汎用関数を用意しており、ライブラリを使う側としてはLensの方が楽だと感じました。