ワイのElmデビュー【後編】

前編はこちらやで。
→[**ワイのElmデビュー【前編】**](https://qiita.com/jzmstrjp/items/eb6fd45fe6324906a71a)

# 前回のあらすじ
純粋関数型言語**Elm**で「**ワイと敵が戦うゲーム**」を作ってみることにした**ワイ**。
果たして、**砕けた床の修理代**は誰が支払うことになるのか・・・！？

# 攻撃ボタンをクリックしたら、敵のHPを減らしたい
ワイ「ええーと、こんな感じでワイの**攻撃ボタン**に**onClick属性**をつけたから・・・」

```elm
button [ onClick WaiAttack ] [ text "攻撃" ]
```

ワイ「**敵さんの攻撃ボタン**にも**onClick属性**をつけとこか」
ワイ「値は**TekiAttack**でええわ」

```elm
button [ onClick TekiAttack ] [ text "攻撃" ]
```

ワイ「こうしておけば、**ワイ**か**敵さん**の**攻撃ボタン**がクリックされたときに」
ワイ「それと**連動**してキャラクターの**状態を更新**できるはずなんや」
ワイ「その**更新内容**を**update関数**の中に書くんやな」

```elm
update msg model =
    -- ここに処理内容を書く
```
ワイ「**update関数**は、**msg**と**model**っちゅう**2つの引数**を受け取るんやな」

ワイ「**model**いう引数には**ワイと敵の状態**が入ってくるんや」
ワイ「**msg**には何が入ってくんねやろ」
ワイ「なになに」
ワイ「なるほどな」
ワイ「**msg**には**WaiAttack**もしくは**TekiAttack**っちゅう**メッセージ**が入ってくるんやな」
ワイ「**ワイの攻撃ボタン**をクリックした場合は**WaiAttackというメッセージ**が」
ワイ「**敵さんの攻撃ボタン**をクリックした場合は**TekiAttackいうメッセージ**が」
ワイ「**msg**いう引数の中に入ってくるんやな」

ワイ「ちゅうことは**msg**の**内容によって**」
ワイ「**敵のHPを減らす処理**をするのか、**ワイのHPを減らす処理**をするのかを**分岐させればええ**訳やから・・・」

```elm
    case msg of
        WaiAttack ->
            -- ここに、敵のHPを減らす処理を書く

        TekiAttack ->
            -- ここに、ワイのHPを減らす処理を書く
```

ワイ「こうやな！」

ワイ「ほな、その**HPを減らす処理**を書いていこか」

ワイ「**敵のHPを減らす**のも**ワイのHPを減らす**のも」
ワイ「**HPを減らす**いう意味では**同じ**やから、**関数として共通化**できそうやな」

ワイ「ほな、**HPを減らすための関数**を書いてこか」
ワイ「**HPを減らす**いうんは、要は**攻撃**のことやから」
ワイ「関数の名前は**attack**にしておこか」

ワイ「まずは**型注釈**から書くんや」
ワイ「**型注釈**いうんは、**関数の使い方**に関する**注釈**や」
ワイ「今回は・・・」
ワイ「**attack**いう関数は、**キャラクター**と**数値**を受け取って、**新しい状態のキャラクター**を返しますよ、と」

```elm
attack : Character -> Int -> Character
```

ワイ「こうやな」
ワイ「しかもこれが**ただの注釈**やなくて、**強制力をもった注釈**なんや」
ワイ「この**注釈の通り**に実装していかんと、**コンパイルエラーで怒られる**んや」

ワイ「**こんな関数を作るで〜**いうて**型注釈**を書くことで」
ワイ「**思考**を**実際のコードに落とし込んで**から関数を書くことになるから」
ワイ「ワイのような**ザコーダー**でも**訳が分からなくなりにくい**な」

ワイ「ほな**attack関数**の中身を書いていこか」
ワイ「**attack関数**は、**キャラクター**に**ダメージ**を与える関数やから」
ワイ「**character**と**damage**という**引数**を受け取ることにしよか」

```elm
attack character damage =
    -- ここに処理内容を書く
```

ワイ「こうやな」
ワイ「ほんで、**キャラクター**の**HP**を**damage**の分だけ減らして」
ワイ「**HPが減った後のキャラクター**を**戻り値**として返してやるんやから・・・」
ワイ「まずは」
ワイ「**let**で**定義**してやるんや」
ワイ「**新しいhpの値**は、**キャラクターのHP**から**ダメージ量の分を差し引いた値**ですよ、と」

```elm
    let
        newHp =
            character.hp - damage
```

ワイ「こうやな」
ワイ「ほんで、次は**let**と対になる**in**や」
ワイ「**キャラクター**の**HP**を**newHp**に**差し替えたやつ**[^1]を」
ワイ「**戻り値**として**返してあげますよ**、と」

```elm
    in
    { character | hp = newHp }
```

ワイ「こうや」
ワイ「**最後に評価された値**が自動的に**戻り値として返される**から、特に**return**とかは**要らへん**のやな」

ワイ「よっしゃ」
ワイ「**キャラクター**と**ダメージ量**を受け取って」
ワイ「**新しい状態のキャラクターを返す**関数」
ワイ「**attack**の出来上がりや！」

```elm
-- attack関数
attack : Character -> Int -> Character
attack character damage =
    let
        newHp =
            character.hp - damage
    in
    { character | hp = newHp }
```

ワイ「ほな次は、この**attack関数**を」
ワイ「**update関数の中**で使っていくねんな」

ワイ「まずは**update関数**の**型注釈**がどないなってんのか見ておこか」

```elm
update : Msg -> Model -> Model
```

ワイ「なるほど」
ワイ「**Msg型**と**Model型**の引数を受け取って」
ワイ「**Model型**の値を返してやればええんやな」
ワイ「**型注釈**が書いてあると**分かりやすいな**・・・」

ワイ「ほな処理を書いていこか」
ワイ「メッセージが**WaiAttack**の場合は、」
ワイ「**ワイの攻撃力**のぶんだけ**敵さんにダメージ**を与えるんや」
ワイ「つまり、さっき作った**attack関数**に**敵さん**と**ワイの攻撃力**を渡してやればええんや」
ワイ「第一引数が**敵さん**で」
ワイ「第二引数が**ワイの攻撃力**やから」

```elm
attack model.teki model.wai.offensivePoint
```

ワイ「こうやな」
ワイ「**attack関数**が返してくるのは**攻撃された後の敵さん**やから、」
ワイ「**attackedTeki**いう名前にしよか」

```elm
let
    attackedTeki =
        attack model.teki model.wai.offensivePoint
```

ワイ「こうやな」
ワイ「ほんで、その**攻撃された敵さん**を」
ワイ「**model**の中の**古い敵さん**と差し替える感じや[^1]」

```elm
in
{ model | teki = attackedTeki }
```

ワイ「逆に、メッセージが**TekiAttack**の場合は、」
ワイ「**敵さんの攻撃力**のぶんだけ**ワイにダメージ**を与えるから」

```elm
let
    attackedWai =
        attack model.wai model.teki.offensivePoint
in
{ model | wai = attackedWai }
```

ワイ「こうやな」
ワイ「**update関数**の出来上がりや！」

```elm
update : Msg -> Model -> Model
update msg model =
    case msg of
        WaiAttack ->
            let
                attackedTeki =
                    attack model.teki model.wai.offensivePoint
            in
            { model | teki = attackedTeki }

        TekiAttack ->
            let
                attackedWai =
                    attack model.wai model.teki.offensivePoint
            in
            { model | wai = attackedWai }
```

ワイ「なんか、結局**同じようなコードを2回**書いてしまった感じがするな」
ワイ「初めてやからしゃあないわ」
ワイ「あとで**リファクタリング**しよか」

ワイ「とりあえずこれで**攻撃ボタンをクリック**したら」
ワイ「**それと連動**して**画面が再描画**されるんちゃうか？」

ワイ「攻撃ボタンを**ポチポチ**、と」
ワイ「おお、**ちゃんと動いとる！**」
ワイ「攻撃するたびに敵やワイのHPが減っていくで！」

ワイ「あ、攻撃し過ぎると**HPがマイナスの値**になってしまうな」
ワイ「でもそれも、どうやって直すか**大体わかる**で」
ワイ「**attack関数**の中で**if式**を書けばええんや」
ワイ「**残HPの量**より**大きいダメージ**を受け取った場合は」
ワイ「キャラクターのHPが**0**になるようにするだけや」

```elm
newHp =
    if character.hp > damage then
        character.hp - damage

    else
        0
```

ワイ「**できたで！**」

# Elmを触ってみた感想

ワイ「**型システムが優秀**やから、間違ったことしてたら**コーディング中に**教えてくれんねん」
ワイ「せやから、実行時に**〇〇がundefinedやで！**みたいな**エラーが出ることが無かった**な」 

ワイ「**型注釈**を書くのは**必須ではない**んやけど、」
ワイ「明示的に型を書いていくことで**頭の中の設計をコード化**できている感じがして**思考が整理**されるし」
ワイ「あとからコードを見直したときにも**何をしようとしているか**が**分かりやすい**な」

ワイ「せやからワイのような**ザコーダー**でも、作ろうと思ったもんが**サクッと**作れたで」
ワイ「・・・今のところ**何のゲーム性もない**ようなゲームやけどな」

ワイ「えっと、作り方の流れとしては・・・」
ワイ「まず管理していきたい**状態**を**Model型**として定義する」
ワイ「ほんで、**モデル=状態**の**初期値**を**init**として設定。」
ワイ「それを、**view関数**を使って表示させんねんな」

ワイ「ほんで、viewの中のボタンに```onClick WaiAttack```とか書いとけば、」
ワイ「そのボタンを**クリックしたのと連動**して**update関数**に```WaiAttack```っていう**メッセージ**が来るから」
ワイ「```WaiAttack```の場合は**ワイの攻撃力のぶんだけ敵のHPを減らすんや！**」
ワイ「ってなことを**update関数**の中で書けばええんやな。」

ワイ「そしたらその**update関数**が返した**新しい状態**が」
ワイ「なんやかんやで**view関数**に渡ってきて」
ワイ「画面が**再描画**されるわけや」

ワイ「こんな感じで、**再代入いう概念がなく**ても**状態の変化**を表現できんねんなー」
ワイ「これが**The Elm Architecture**いうやつか」

ワイ「**状態を変更**する処理が**update関数の中に集まる**形になるから」
ワイ「**一括管理**できてる感じがええな」

ワイ「いろんなオブジェクトが**アプリケーションの状態を変え放題**やったりすると」
ワイ「**思わぬ作用**が起こって」
ワイ「**1回攻撃しただけ**なのに**HPが2回分**減っとった！」
ワイ「なんてことも起こりうるからな」
ワイ「さすが、**Redux**や**Vuex**にも影響を与えた**Elmちゃん**やな」

ワイ「うーん、なんやろ・・・**楽しかったなぁ**」
ワイ「**仕事でも使いたいわぁ**・・・」

# 仕事でElmを使うために
ワイ「ほな、まずは会社のみんなに**コンビニのコーヒーでも奢って**」
ワイ「**精神的買収**をしとこか」
ワイ「そういう**根回し**は大切や」
ワイ「よ〜し、**エエとこ見せるで！**」

ワイ「お〜い、みなさん」
ワイ「ちょっとコーヒーでも買ってきますわ」
ワイ「**ええて！ええて！**ワイが**奢ります**さかい！」
ワイ「ええと、何人分や？」
ワイ「**0, 1, 2, 3**・・・**3人分やな！**」

ハスケル子「やめ太郎さん」
ハスケル子「**現実のものを0から数えないでください**」
ハスケル子「**1つ足りなく**なります」

社長「（やっぱこいつ**アホ**やな・・・）」

**〜Fin〜**

# 後日談: 砕けた床はどうなったか
社長「下のフロアもよく見えるし、このままでええわ！」

# 追記
[**仕事でElm書きたい**](http://namonaki-cluster.herokuapp.com/clusters/85)クラスタを作ってみました。よかったらご参加くださいやで。

[^1]: 実際には既存のレコードの一部を差し替えるのではなく、新しいレコードが生成されます。レコードとはJavaScriptでいうオブジェクトのようなものです。