はじめに
Elmのアドベントカレンダー20日目に申し込みさせていただきました。まったくElmを触らない人間が投稿するのはかなり申し訳ないのですが、書かせていただきます。
ついでにお詫びなのですが、我ながら(クオリティ含めて)クソみたいなアプリを作ってしまいました。すいませんがどうかご査証ください。
内容
**妹が鬼になってしまった!**そんなときに2秒以内で「闘う」か「闘わない」のどちらかを選択しないと天狗のおじさんに怒られてしまうアプリです。元ネタを知らない方は各自で調べてみると良いかと思います。
こちらで試すことができます。
実装
まず変数として定義しておきたいのは、天狗のおじさんの回答(tenguAnswer
)と2秒立ってしまったかどうか(isTimeout
)の2つなのでそれぞれのモデルを定義します。
type alias Model =
{ tenguAnswer : String
, isTimeout : Bool
}
また、メッセージとして定義しておきたいのは判断(Decision
)とその内容(Judge
)なのでそれぞれを定義します。
type Judge
= Late
| Fast
| Bad
type Msg
= Decision Judge
- Late
- 2秒過ぎてしまった場合のメッセージ
- Fast
- 迷いなく「闘う」と選択した場合のメッセージ
- Bad
- 「闘わない」などと甘い選択をした場合のメッセージ
とりあえずそれぞれを簡単に定義しました。次に2秒すぎた場合、JavaScriptのsetTimeout
みたいなことを実装する方法がよくわからなかったので@sandさんの[こちらの記事] (https://qiita.com/sand/items/7504165dccf54848a27b)を参考に実装してみました。
その上で、モデルの定義はこんな感じで実装しました。
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
というメッセージを渡すようにしました。あとの実装は完全に成り行きにまかせて行ったのでコード全文貼ってしまいます。
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でタイマーを使った処理の実装が思ったよりも難しくて、もう少しライブラリに関しての理解が必要だなと感じた次第です。。。