フラグ
フラグは初期化と同時にElmになんらかの値を渡す方法です。
よくある使い方は、APIキーや、環境変数、それにユーザーが作ったデータを渡したいときなどでしょう。Elmでコマンドやメッセージを発行しない最初のレンダリング時に、フラグの値に応じて動的にHTMLを生成することができます。また、このlocalStorageの例のようにウェブブラウザーに保存されたデータを読み取るときにも役立ちます。
HTMLでのフラグ
このHTMLは前のページに出てきたものと基本的に同じですが、Elm.Main.init()関数に追加の引数flagsを渡しているところが違います。
<html>
<head>
<meta charset="UTF-8">
<title>Main</title>
<script src="main.js"></script>
</head>
<body>
<div id="myapp"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('myapp'),
flags: Date.now()
});
</script>
</body>
</html>
この例では現在時刻をミリ秒として渡していますが、JSONとしてデコードできるものであればどんなJavaScriptの値でもフラグに使うことができます。
Note: この追加のデータが『フラグ』と呼ばれているのは、それがコマンドラインフラグのようなものだからです。
elm make src/Main.elmを実行するとき、--optimizeや--output=main.jsのようなフラグを追加して、その動作をカスタマイズすることができます。フラグもそれと似たようなものです。
Elmでのフラグ
Elmの側でフラグを扱うために、init関数に少し手を加える必要があります:
module Main exposing (..)
import Browser
import Html exposing (Html, text)
-- MAIN
main : Program Int Model Msg
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model = { currentTime : Int }
init : Int -> ( Model, Cmd Msg )
init currentTime =
( { currentTime = currentTime }
, Cmd.none
)
-- UPDATE
type Msg = NoOp
update : Msg -> Model -> ( Model, Cmd Msg )
update _ model =
( model, Cmd.none )
-- VIEW
view : Model -> Html Msg
view model =
text (String.fromInt model.currentTime)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
ここで重要なポイントはただ一つ、init関数が引数としてIntを受け取っていることです。このように、JavaScriptの世界から渡されたフラグはElmからすぐにそのまま利用できます。フラグを受け取った後は、モデルに入れておいたり、コマンドを実行するのに使ったり、必要なら何にでも使うことができます。
もっと面白いフラグの使い方を知りたいなら、このlocalStorageの例を見てみてください!
フラグの検証
しかし、Intをフラグとして受け取るようにinitを定義したにも関わらず、Elm.Main.init({ flags: "haha, what now?" })というように初期化しようとするような人がいたら、いったい何が起こるのでしょうか。
Elmはそのような場合に対してもチェックを行い、フラグの型が期待していた通りであることを保証してくれます。もしこのチェックがなければ、どんなデータでも渡すことができてしまい、Elm側では実行時エラーが起きてしまうでしょう!
フラグとして渡すことのできる型には、次のように様々な型があります。
BoolIntFloatStringMaybeListArray- tuples
- records
Json.Decode.Value
フラグを厳密に制御できるように、常にJson.Decode.Valueを使うようにしている人もたくさんいます。どんな変な値であっても受け取り、それが予想外のデータであってもうまく修正することができるように、Elm側のコードでデコーダを書いているのです。
JSON.Decode.Value 以外の型をフラグとして渡す機能は、実はJSONデコーダーを使う方法が発明されるよりも前の時代に使われていたものです。
これらの型をフラグとして渡すには、いくつか注意しないといけないことがあります。次の例では、渡そうとしているフラグの型それぞれについて、いろいろなJavaScriptの値を渡すとそれぞれ何が起こるのかを示しています。
init : Int -> ...0=>07=>73.14=> error6.12=> error
init : Maybe Int -> ...null=>Nothing42=>Just 42"hi"=> error
init : { x : Float, y : Float } -> ...{ x: 3, y: 4, z: 50 }=>{ x = 3, y = 4 }{ x: 3, name: "Tom" }=> error{ x: 360, y: "why?" }=> error
init : (String, Int) -> ...["Tom", 42]=>("Tom", 42)["Sue", 33]=>("Sue", 33)["Bob", "4"]=> error["Joe", 9, 9]=> error
もしこのような変換がひとつでもうまくいかない場合は、JavaScript側でエラーが起こることに注意してください! Elmでは『フェイルファスト』(fail fast)の原則をとっています。Elmコード側でエラーを起こすのではなく、可能な限り早く問題を報告するということです。これはフラグにJson.Decode.Valueを使うのを好む人がいる理由のひとつにもなっています。JavaScript側でエラーが起きるより、デコーダでこの変な値を受け取ることで、何らかのフォールバックが実装されているのを保証するほうがいいということです。