初心者Elm入門（その４：削除機能付きToDoリスト）

# はじめに
[初心者Elm入門その３](https://qiita.com/saiwa8951/items/82dc32334751bcdca213)で紹介したElmアーキテクチャを使ってもう少し複雑な例に取り組んでみよう。

# 削除機能付きToDoリスト
完成画面はこのようになる。
<img width="254" alt="スクリーンショット 2019-04-25 11.22.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338910/69cf6027-5478-0ba7-c57c-86b2451fa615.png">
入力フィールドに文字を入力すると「Add ToDo」ボタンが押せるようになり、ToDoリスト中の文字をクリックすることで削除できる。
さっそくコードを見よう。
まずは自力バージョン。
のちに改良バージョンも示す。

```Elm:ToDoList.elm
module Main exposing (main)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)


main : Program () Model Msg
main =
    Browser.sandbox
        { init = init
        , update = update
        , view = view
        }



-- MODEL


type alias Model =           -- 今回のModelはレコード。
    { input : String         -- 入力フィールド中の文字列を保持する。
    , memos : List String    -- 文字列を要素とするリスト。
    }                        -- 上記の例では memos = ["モンドール", "ワイン", "醤油", "卵", "食パン"]
                             -- のようになる。submitされた文字列を格納する。

init : Model                 -- input初期値は""、memos初期値は[]
init =
    { input = ""
    , memos = []
    }



-- UPDATE


type Msg
    = Input String
    | Submit
    | Done Int


update : Msg -> Model -> Model
update msg model =
    case msg of
        Input input ->                   -- 入力フィールドの文字が変わるたびに
            { model | input = input }    -- inputとして受け取る。

        Submit ->                        -- 「Add ToDo」ボタンが押されると
            { model
                | input = ""             -- 入力フィールドを空にし
                , memos = model.input :: model.memos    -- memosの先頭にinput文字列を加える。
            }

        Done done ->              -- done(Int型)番目の要素がクリックされると
            { model
                | memos = List.take done model.memos ++ List.drop (done + 1) model.memos
            }            -- 0~(done-1)番目までのmemosと
                         -- (done+1)~最後までのmemosが連結されたリストを
                         -- 新しいmemosとする。

-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ Html.form [ onSubmit Submit ]
            [ input [ value model.input, onInput Input ] []
            , button
                [ disabled (String.length model.input < 1) ]    -- inputのlengthが1未満だとボタンが押せない。
                [ text "Add ToDo" ]
            ]
        , ul [] (viewMemo model.memos)    -- viewMemo関数の引数としてmemos(List String)をとる。
        ]


htmlFunc : ( Int, String ) -> Html Msg    -- ややこしいので地の文で説明します。
htmlFunc tuple =
    let
        i =
            Tuple.first tuple

        memo =
            Tuple.second tuple
    in
    li [ onClick (Done i) ] [ text memo ]


viewMemo : List String -> List (Html Msg)
viewMemo memos =
    let
        memoIndexPair =
            List.indexedMap Tuple.pair
    in
    List.map htmlFunc (memoIndexPair memos)    -- (*)

```
(*) memoIndexPair関数はList Stringを渡すと`[(０, "文字列0"), (1, "文字列1"), ...]`というタプルを要素とするリストを返してくれる。
例では`memos = ["モンドール", "ワイン", "醤油", "卵", "食パン"]`なので、`memoIndexPair memos`で`[(0, "モンドール"), (1, "ワイン"), (2, "醤油"), (3, "卵"), (4, "食パン")]`となる。
これに対してList.mapで各々の要素についてhtmlFuncを適用する。
htmlFuncはタプルの0番目の要素をiとし、１番目の要素をmemoとしてHtml Msgである`li [ onClick (Done i) ] [ text memo ]`を作る。
これで完成！......なんだけど、会社の先輩が「もっと簡単に書けるよ！」って教えてくれたのでちょっと改良する。

# 削除機能付きToDoリスト(改)
以下改良バージョン。
変更したのはややこしかったhtmlFuncとviewMemoの部分のみ。

```Elm:ToDoList2.elm
module Main exposing (main)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)


main : Program () Model Msg
main =
    Browser.sandbox
        { init = init
        , update = update
        , view = view
        }



-- MODEL


type alias Model =
    { input : String
    , memos : List String
    }


init : Model
init =
    { input = ""
    , memos = []
    }



-- UPDATE


type Msg
    = Input String
    | Submit
    | Done Int


update : Msg -> Model -> Model
update msg model =
    case msg of
        Input input ->
            { model | input = input }

        Submit ->
            { model
                | input = ""
                , memos = model.input :: model.memos
            }

        Done done ->
            { model
                | memos = List.take done model.memos ++ List.drop (done + 1) model.memos
            }



-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ Html.form [ onSubmit Submit ]
            [ input [ value model.input, onInput Input ] []
            , button
                [ disabled (String.length model.input < 1) ]
                [ text "Add ToDo" ]
            ]
        , ul [] (viewMemo model.memos)
        ]


htmlFunc : Int -> String -> Html Msg
htmlFunc i memo =
    li [ onClick (Done i) ] [ text memo ]


viewMemo : List String -> List (Html Msg)
viewMemo =
    List.indexedMap htmlFunc
```
とてもすっきりした。
しかし初心者としてはなぜ動くのかが分からない(分からなかった)。
まずviewMemoに引数がないのが意味が分からない。
これは関数と関数を合わせて関数を作るというものらしい(部分適用)。
具体例を示す。

```Elm
> (*)
<function> : number -> number -> number
> (*) 2 3
6 : number
> f = (*) 2
<function> : number -> number
> f 3
6 : number
```
(*)というのは第1引数と第2引数のかけ算をする関数だ。

`f = (*) 2`とすると(*)の第1引数が２に固定された関数fを作ることができる。
よって`f 3`は`6`となる。
ここで

```Elm
> f = (*) 2
<function> : number -> number
> List.map
<function> : (a -> b) -> List a -> List b
> List.map f [1, 2, 3, 4, 5]
[2,4,6,8,10] : List number
> g = List.map f
<function> : List number -> List number
> g [1, 2, 3, 4, 5]
[2,4,6,8,10] : List number
```
`List.map 関数 リスト`はリストの要素ひとつひとつに関数を適用して展開するものである。
上記例では`[1, 2, 3, 4, 5]`の各要素を２倍して展開する。
`g = List.map f`と置くことでfしながらmapもしてくれる関数をgと呼ぶことにしたということ。

ToDoList.elmに戻ろう。
List.indexedMapの定義は`(Basics.Int -> a -> b) -> List.List a -> List.List b`なので、この第1引数を`Int -> String -> Html Msg`であるhtmlFuncに固定してやる。
そうすることで、List Stringが来たときにList (Html Msg)を返す関数viewMemoが出来る。
viewMemoはList.indexedMapとhtmlFuncの機能を同時に行ってくれる関数と見ることが出来るのでmemosの要素番号と要素が取得出来て`li [ onClick (Done i) ] [ text memo ]`が得られる。

# 試し方
- 適当なディレクトリを作る。
- `elm init`する。
- srcというディレクトリができているのでsrc内にToDoList.elmを置く。
- `elm make src/ToDoList.elm --output=todolist.html`する。(--outputは必須ではないが指定しないとindex.htmlという名前でコンパイルされる。)
- todolist.htmlが出来るのでブラウザで開く。

# 初心者Elmシリーズ
- [初心者Elm入門（その１：インストール〜Hello World!）](https://qiita.com/saiwa8951/items/befa0d1683f16a992b67)
- [初心者Elm入門（その２：文法）](https://qiita.com/saiwa8951/items/cf0067447c1189af1175)
- [初心者Elm入門（その３：Elmアーキテクチャ）](https://qiita.com/saiwa8951/items/82dc32334751bcdca213)