フラグ
フラグは初期化と同時に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側では実行時エラーが起きてしまうでしょう!
フラグとして渡すことのできる型には、次のように様々な型があります。
Bool
Int
Float
String
Maybe
List
Array
- tuples
- records
Json.Decode.Value
フラグを厳密に制御できるように、常にJson.Decode.Value
を使うようにしている人もたくさんいます。どんな変な値であっても受け取り、それが予想外のデータであってもうまく修正することができるように、Elm側のコードでデコーダを書いているのです。
JSON.Decode.Value
以外の型をフラグとして渡す機能は、実はJSONデコーダーを使う方法が発明されるよりも前の時代に使われていたものです。
これらの型をフラグとして渡すには、いくつか注意しないといけないことがあります。次の例では、渡そうとしているフラグの型それぞれについて、いろいろなJavaScriptの値を渡すとそれぞれ何が起こるのかを示しています。
init : Int -> ...
0
=>0
7
=>7
3.14
=> error6.12
=> error
init : Maybe Int -> ...
null
=>Nothing
42
=>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側でエラーが起きるより、デコーダでこの変な値を受け取ることで、何らかのフォールバックが実装されているのを保証するほうがいいということです。