判断が遅いと天狗のおじさんに怒られるElmアプリを作った

# はじめに

Elmのアドベントカレンダー20日目に申し込みさせていただきました。まったくElmを触らない人間が投稿するのはかなり申し訳ないのですが、書かせていただきます。
ついでにお詫びなのですが、我ながら（クオリティ含めて）クソみたいなアプリを作ってしまいました。すいませんがどうかご査証ください。

# 内容

**妹が鬼になってしまった！**そんなときに2秒以内で「闘う」か「闘わない」のどちらかを選択しないと天狗のおじさんに怒られてしまうアプリです。元ネタを知らない方は各自で調べてみると良いかと思います。

![sc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/653efb8a-9944-9494-b7bd-8881c4cf8024.png)

[こちら](https://ufoo68.github.io/handan-no-hayasa/)で試すことができます。

# 実装

まず変数として定義しておきたいのは、天狗のおじさんの回答（`tenguAnswer`）と2秒立ってしまったかどうか（`isTimeout`）の２つなのでそれぞれのモデルを定義します。

```elm
type alias Model =
    { tenguAnswer : String
    , isTimeout : Bool
    }
```

また、メッセージとして定義しておきたいのは判断（`Decision`）とその内容（`Judge`）なのでそれぞれを定義します。

```elm
type Judge
    = Late
    | Fast
    | Bad

type Msg
    = Decision Judge
```

- Late
  - 2秒過ぎてしまった場合のメッセージ
- Fast
  - 迷いなく「闘う」と選択した場合のメッセージ
- Bad
  - 「闘わない」などと甘い選択をした場合のメッセージ

とりあえずそれぞれを簡単に定義しました。次に2秒すぎた場合、JavaScriptの`setTimeout`みたいなことを実装する方法がよくわからなかったので@sandさんの[こちらの記事] (https://qiita.com/sand/items/7504165dccf54848a27b)を参考に実装してみました。
その上で、モデルの定義はこんな感じで実装しました。

```elm
type alias Model =
    { tenguAnswer : String
    , isTimeout : Bool
    }


longTask : Task Never Judge
longTask =
    Process.sleep 2000
        |> Task.map (always Late)


init : () -> ( Model, Cmd Msg )
init _ =
    ( Model "" False
    , Task.perform Decision longTask
    )
```

この`longTask`という関数が2秒経過後に`Late`というメッセージを渡すようにしました。あとの実装は完全に成り行きにまかせて行ったのでコード全文貼ってしまいます。

```elm
module Main exposing (..)

import Browser
import Html exposing (Html, button, div, h1, img, text)
import Html.Attributes exposing (hidden, style, src)
import Html.Events exposing (onClick)
import Process
import Task exposing (..)



-- MODEL


type alias Model =
    { tenguAnswer : String
    , isTimeout : Bool
    }


longTask : Task Never Judge
longTask =
    Process.sleep 2000
        |> Task.map (always Late)


init : () -> ( Model, Cmd Msg )
init _ =
    ( Model "" False
    , Task.perform Decision longTask
    )



-- UPDATE


type Judge
    = Late
    | Fast
    | Bad


type Msg
    = Decision Judge


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Decision judge ->
            case judge of
                Bad ->
                    ( { model
                        | tenguAnswer =
                            if not model.isTimeout then
                                "判断が甘い"

                            else
                                model.tenguAnswer
                        , isTimeout = True
                      }
                    , Cmd.none
                    )

                Fast ->
                    ( { model
                        | tenguAnswer =
                            if not model.isTimeout then
                                "判断が早い"
                            else
                                model.tenguAnswer
                        , isTimeout = True
                      }
                    , Cmd.none
                    )

                Late ->
                    ( { model
                        | tenguAnswer =
                            if not model.isTimeout then
                                "判断が遅い"

                            else
                                model.tenguAnswer
                        , isTimeout = True
                      }
                    , Cmd.none
                    )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none



-- VIEW


view : Model -> Html Msg
view model =
    div
        [style "text-align" "center"]
        [ h1 [] [ text "妹が鬼になってしまった！" ]
        , button [ onClick (Decision Fast), style "font-size" "2em" ] [ text "闘う" ]
        , button [ onClick (Decision Bad), style "font-size" "2em" ] [ text "闘わない" ]
        , h1 [ hidden (not model.isTimeout) ] [ text model.tenguAnswer ]
        , img [ src "./tengu.png", hidden (not model.isTimeout) ] []
        ]


main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }

```

updateの処理が結構汚くなってしまいました。「2秒経過するまでにどちらかのボタンを選択すると`Late`というメッセージの送信がストップされる」というような実装をしたかったのですが方法がわからなかったので、if文をつかって`tenguAnswer`が書き換わるのを止めるという実装にしました。このあたりの良い実装方法があれば教えていただきたいです。。。

# おわりに

すいません、かなりボリュームの小さい記事になってしまいました。Elmでタイマーを使った処理の実装が思ったよりも難しくて、もう少しライブラリに関しての理解が必要だなと感じた次第です。。。