[Elm]カウンタープログラムを短く書きたい

先日、このようなアプリを作りました。
![kiji_stimer01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554706/3f3486af-ee67-808f-008d-b12c275e3161.png)
[こちらです](https://github.com/GoldentTuft/stimer)

スタート日時と、終了日時を設定するUI部分です。

書き始めてMsgが大量に必要になりそうだという事に気づきました。
日の設定にDownとUpの2つ、時の設定に4つ、分の設定に4つ。
それがスタートと終了の2つなので20個。

``` Elm
type Msg
    = DownStartDays
    | UpStartDays
    | DownEndDays
    | UpEndDays
    -- ....
```
書き始めて嫌になりました。

最初はたとえば「時」だけ作り始め、Msg,update,viewのどれも長くなりそうでなんとかしなければと思いました。
## メッセージに値を含める

まずは、`DownStartHours`, `DownStartHoursFive`などとせずにメッセージに値を含めることにしました。

``` Elm
type Msg
    = DownStartHours Int
update msg model =
    case msg of
        DownStartHours step ->
            ( downHours step model.startEnd.start
                |> setStartPoint model
            , Cmd.none
            )
        DownEndHours step ->
            ( downHours step model.startEnd.end
                |> setStartPoint model
            , Cmd.none
            )
view model =
    div []
        [ button [ onClick (DownStartHours 1) ] [ text "⬇" ]
        , button [ onClick (DownStartHours 5) ] [ text "5⬇" ]
        ]
```

こんな感じです。

すぐに止め、StartかEndかも値で渡すようにしました。

``` Elm
type Msg
    = DownHours PointType  Int
type PointType
    = Start
    | End
update msg model =
    case msg of
        DownHours ptype step ->
            case ptype of
                Start ->
                    ( downHours step model.startEnd.start
                        |> setStartPoint model
                    , Cmd.none
                    )

                End ->
                    ( downHours step model.startEnd.end
                        |> setEndPoint model
                    , Cmd.none
                    )
view model =
    div []
        [ button [ onClick (DownHours Start 1) ] [ text "⬇" ]
        , button [ onClick (DownHours Start 5) ] [ text "5⬇" ]
        ]
```

あまり変わっていない感じがしますが、view部分は短くなりました。

``` Elm
view model =
    div []
        [ viewHours Start model.startEnd.start.hour
        , viewHours End model.startEnd.end.hour
        ]
viewHours : PointType -> Int -> Html Msg
```

このようにできるようになったので。

## Down,UpからUpdateと抽象化してみた
最初から`Down 5`や`Up 5`とせずに、`Update 5`や`Update -5`とすればとは思っていました。
しかし、「時」のカウントアップでは23を超えたら0に、カウントダウンでは0を下回れば23にと愚直にしていたのが悩みどころでした。(これもなんとかしたい)

都合よく、ループする数字を作ってみました。
[こちらです。](https://github.com/GoldentTuft/looper)


``` Elm
import Looper as Lp
updateHours : Int -> StartEnd.Point -> StartEnd.Point
updateHours amount point =
    { point | hour = Lp.new point.hour 0 23 |> Lp.add amount |> Lp.get }

update msg model =
    UpdateHours amount ptype ->
        case ptype of
            Start ->
                ( updateHours amount model.startEnd.start
                    |> setStartPoint model
                , Cmd.none
                )

            End ->
                ( updateHours amount model.startEnd.end
                    |> setEndPoint model
                , Cmd.none
                )

```

こんな感じになり、なかなか短くなったと思います。

## modelを渡したい(けど)

``` Elm
update msg model =
    UpdateHours amount ptype ->
        case ptype of
            Start ->
                updateHours amount model.startEnd.start
            End ->
                updateHours amount model.startEnd.end
--
UpdateHours 1 Start
UpdateHours 1 End
```

このようにせず

``` Elm
update msg model =
    UpdateHours amount date ->
        updateHours amount date
--
UpdateHours 1 model.startEnd.start
UpdateHours 1 model.startEnd.end
```
このようにしてしまえば、と思いそうですが、これは危なそうです。
[[Elm] Msg に更新後の値を含めるのは要注意](https://qiita.com/jinjor/items/772d8765f5743cc24e9a)

## ところで
削除機能付きのTodoアプリみたいなものを作ったことがあるのですが、今思えばあれも少々危ういのかもしれません。
List.indexedMapをこんなふうに利用して削除機能を実現できるのか!、と思いましたが。

```
[0 1 2 3 4 5]
List.indexedMapを利用し3と4を削除する
---
> Del 3
> Del 4
update [0 1 2 3 4 5] =
	Del 3
update [0 1 2 4 5] =
	Del 4
=> [0 1 2 4]
---
[0 1 2 5]を望んていたはず。
---
> Del todos.id
これならよさそう
```
実際には高橋名人のような連打でもなんの問題もないのだろうと思います。