プロジェクトにてフロントをElmで、バックエンドは Haskell を使うことになりました。
等を参考に勉強中です。
今回は自分の中での整理も含め、Browser パッケージについてまとめます。
結論 Browserは目的に合わせて関数を選ぶ
Browser
の4つの関数はそれぞれ引数レコードの型が異なります。
例えば既存のアプリケーションにElmで作成した要素を部分適用する場合にはsandbox
やelement
を、
一からSPA作ろうと思ったらapplication
を選択するなど、
コンパイル後のhtmlやjsをどのように使用するか、またそのファイルでやりたいことに合わせてsandbox
、element
、document
、application
から選択してmain
関数を定義します。
基本的なElmファイルの構成
↓ボタン・An Introduction to Elmから拝借
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model = Int
init : Model
init =
0
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
Elmのガイドにて、真っ先にサンプルとして紹介されているプログラムとなります。
ガイドに詳しくは記載されていますが、Elmの基本的なパターンは
- ユーザーからの入力を待ちます
- updateにメッセージを送ります
- 新しいModelを生成します
- view関数を呼び出して新しいHTMLを取得します
- 画面上に新しいHTMLを表示します
- 繰り返します!
と記載されています。
ここで、今回の本題のBrowser
ですが、main
関数で登場しています。
このmain
は、
main
はElmでは特別な値で、画面に何を表示するかを記述します。この例では、アプリケーションをinit
で初期化して、view
関数ですべてを画面に表示し、ユーザーからの入力をupdate
関数に渡します。これがプログラムの大まかな概要だと考えてください。
とのことで、作りたいアプリによってmain
をBrowser
を使って定義する必要があります。
Browser
Browser
には関数が4つ用意されています。
- sandbox
:ボタンやチェックボックスなど入力への反応
- element
:HTTPやJSとの相互運用などの外界との通信
- document
:<title>
と <body>
を操作
- application
:SPAを作る
との説明でこれらを必要に応じて使い分けていきますが、それぞれの関数の型の差を見ていきます。
Browser.sandbox
sandbox :
{ init : model
, view : model -> Html msg
, update : msg -> model -> model
}
-> Program () model msg
init
、view
、update
から、Program
を返します。
今回は深追いしませんが、Program
で型変数flags
、model
、msg
をもとにプログラムの状態を返している、くらいの認識で進みます。
()
はユニット型と呼ばれ、空の値が入るという認識で進みます。
Browser
の関数の中ではレコードの要素が一番少ないです。
後述するflagsやsubscriptions等を使用する場合は、そもそもsandbox
の引数の型に含まれていないため、
例えば、
...
-- MAIN
main =
Browser.sandbox
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
...
はコンパイルが通りません。
Browser.element
element :
{ init : flags -> ( model, Cmd msg )
, view : model -> Html msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
}
-> Program flags model msg
先ほどのsandbox
と比べ、
Cmd
、flags
、Sub
という要素が追加されています。
概要の説明となりますが、elementでは、下記4通りの方法で外界と通信することが出来ます。
一つのHTML elementを返します。それを別のJSのプロジェクトに追加したりできます。
- Cmd
:HTTPなど、Elmランタイムに対し命令を出せます。
- Sub
:Websocketでつないだ先からのメッセージの購読など、イベントを"subscribe"できます。
- flags
:初期化と同時にJSからElmに何らかの値を渡します。
- ports
:JSとの相互運用でクライアント-サーバーの関係を設定できます。
Browser.Document
document :
{ init : flags -> ( model, Cmd msg )
, view : model -> Document msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
}
-> Program flags model msg
element
と比較すると、view
関数でHTML msg
ではなく、Document msg
を返しています。
Document
は型エイリアスで、
type alias Document msg =
{ title : String
, body : List (Html msg)
}
と定義されており、Browser.element
では単一のHTMLの要素を返すのに対し、
Browser.document
では"title"と、"body"を含めたコントロールを返すことが出来ます。
Browser.application
application :
{ init : flags -> Url -> Key -> ( model, Cmd msg )
, view : model -> Document msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, onUrlRequest : UrlRequest -> msg
, onUrlChange : Url -> msg
}
-> Program flags model msg
document
と比較すると、init
の型にUrl
とKey
の引数、
onUrlRequest
、onUrlChange
が追加されています。
これらにより、URLの変更を管理できるアプリケーションを作成します。
init
では、アプリ起動時に最初のURLを取得します。
onUrlRequest
では、リンクが押下された際に実行する関数をupdate
関数内に設定できます。
onUrlChenge
では、Urlが変更されたときに実行する関数をupdate
関数内に設定できます。
終わり
プロジェクト、フロントの構成ってどうしよ、とか考えながら見ていたのですが、
モジュールの構造化で面白いことが書かれてました。
防衛本能
The Life of a Fileで私は、その人がElmへと入ってきたときに迷子にさせてしまうような、JavaScriptの神話的知識について指摘しました。・
『ファイルは短くしよう』JavaScriptでは、とてもやっかいなバグを引き起こす、隠れた状態変化が起きやすいです。しかしElmでは、そのようなことは起こりえません!ファイルは2000行の長さになってもいいですし、それでもそのようなバグは起きないのです。・
『最初から正しいアーキテクチャにしよう』JavaScriptではリファクタリングはとてつもなく危険で、最初からすべて書き直したほうが手っ取り早いでしょう。でもElmでは、リファクタリングは簡単で安全です!20の異なるファイルでも自信を持って変更することができます。これらの『防衛本能』は問題からあなたを守ってくれると思うかもしれませんが、そんな問題はそもそもElmには存在しないのです。これらの神話を頭の片隅に置いたままにしても、防衛本能に振り回されるよりはいいでしょう。でも、ファイルが400行、600行、800行と増えていくのを見た時に、このJavaScriptの神話はとても落ち着かない気持ちにさせてくるのがわかりました。行数の限界をもっと押し上げておくのをお勧めします! どこまで行けるのか試してみてください。コメントヘッダを使ってみたり、補助関数を作ってみましょう。でもそれらをすべてひとつのファイルに入れたままにしておいてください。これは自分自身で経験する価値があります!
1000行越えのmain.elm
一刀流でもいい…ってコト!?