[Elm] Task, Random, Httpがわからない？その前にThe Elm Architectureを確実にマスターしよう

Elm入門して、しばらくしてから聞く疑問としてTask, Random, Httpいずれかのものがわからない。と言う話をよく聞きます。これらは他の言語では全くバラバラの概念かもしれません。しかしElmの道 The Elm Architectureで同様に処理される部類ものたちです。逆に言えばこの道に乗らずにこれらの概念は扱うことができません。The Elm Architectureさえ理解できれば、あとは各機能のどの部分がわからないのかを把握すれば、乗り越えるのは難しくないと思います。わからない概念の組み合わせこそが難しいと思わせる要因だと考えます。

今回の[コード](https://github.com/ababup1192/elm-cmds)です。

# 今回作るもの

とにかく題材をシンプルにしてみました。実行結果として、

- 非同期で取得できる固定文字列
- 1-100までの乱数
- ジョークAPIで取得してきた前振り

が表示されます。

<img width="423" alt="スクリーンショット 2020-06-02 21.53.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48687/9792a054-6b61-bd45-369b-f55fbc9614ca.png">

ここから先、断片的なコードを載せていきます。理解がしやすい順番に載せるので、わからないものが出てきた場合にそこが理解の障壁になっている箇所だと思われます。まずはそこの理解の穴を塞いでいきましょう。

## Modelとview

結果を格納するModelと表示部分のviewになります。ここでつまずいた場合には、Elmの基礎的なデータ型もしくは関数呼び出し方法の理解に穴があるようです。

```elm
type alias Model =
    { asyncString : String
    , randomNumber : Int
    , joke : String
    }

view : Model -> Html Msg
view model =
    main_ [ class "ly_cont" ]
        [ p [] [ text <| "async: " ++ model.asyncString ]
        , p [] [ text <| "random: " ++ String.fromInt model.randomNumber ]
        , p [] [ text <| "joke: " ++ model.joke ]
        ]
```

## Msgとupdate

次にModelを書き換えるためのMsgとupdateになります。ここでつまずいた場合には、カスタムタイプとパターンマッチ、レコード型の理解に穴があるようです。Httpの場合、失敗可能性があるためResult型を使っていますが、これもカスタムタイプの応用になります。

```elm 
type Msg
    = GotAsyncString String
    | GotRandomNumber Int
    | GotJoke (Result Http.Error String)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotAsyncString str ->
            ( { model | asyncString = str }, Cmd.none )

        GotRandomNumber num ->
            ( { model | randomNumber = num }, Cmd.none )

        GotJoke res ->
            case res of
                Ok joke ->
                    ( { model | joke = joke }, Cmd.none )

                Err _ ->
                    ( { model | joke = "Error" }, Cmd.none )
```

## Cmdの生成と呼び出し

なぜTask, Random, Httpこれらの機能をひとまとめにして、この記事では紹介しているのでしょうか？ それは今までのThe Elm Architectureコードと**副作用を起こす**という点において、共通しているからです。これを理解さえしてしまえば、今後これ以上に難しい仕組みは出てきません。Elmで副作用は*Cmd msg*型を使用しますが、*msg*型を返すということだけに着目さえしてしまえば、各々の機能の関数が*Msg*型の値を欲していることに気づくはずです。今回で言えば、

- GotAsyncString
- GotRandomNumber
- GotJoke

が、Msg型に当たります。あとは、`Task x a`型がわからないのか、`Random.Generator a`型がわからないのか、Http RequestもしくはJson Decoderの書き方がわからないのかに的を絞ることができます。基本的にはリファレンスが親切なためリファレンスを読むか、それらについて解説した記事をいくつか用意しています。

```elm 
getAsyncString : Cmd Msg
getAsyncString =
    Task.perform GotAsyncString <|
        Task.succeed "AsyncString"


getRandomNumber : Cmd Msg
getRandomNumber =
    Random.generate GotRandomNumber <| Random.int 0 100


getJoke : Cmd Msg
getJoke =
    Http.get
        { url = "https://official-joke-api.appspot.com/random_joke"
        , expect = Http.expectJson GotJoke <| JD.field "setup" JD.string
        }
```

ここまで理解してしまえばあとは呼び出しです。update関数のタプルの第二引数にCmd Msg型の関数呼び出しをしたり、init時に呼び出したりタイミングの問題です。

```elm
init : () -> ( Model, Cmd Msg )
init _ =
    ( { asyncString = ""
      , randomNumber = -1
      , joke = ""
      }
    , Cmd.batch
        [ getAsyncString
        , getRandomNumber
        , getJoke
        ]
    )
```

# まとめ

何かにつまずいてしまった場合には、何につまづいたか問題の因数分解が必要です。今回は副作用という点に着目して因数分解をしてみました。ここを乗り越えてしまえばElmで実用的なアプリケーションが自由自在にできてしまいます。是非乗り越えて楽しみましょう！それでは良いElmライフを！