最初に
Svelte の話はしません、Elm の紹介をします。
ことの発端
プロジェクトで Svelte を使ってみたく少し調査、コード書いてみたりなどしました。
色々みていると、推してることの一つとして仮想 DOM を使用してないということをよく見かけました。
周りの人たちと、そもそも仮想 DOM がいいからそっちがいま主流になった経緯があると思うんだけど、逆に前に戻ってるってことなの?とか、なんで仮想 DOM じゃないことがいいの?みたいな話になりもした。
そこについては、Svelte おじさんの主張 Virtual DOM is pure overhead が仮想 DOM が早いとか遅いとかではなく、State Driven な UI 開発をする上で state のことをあんま考えずに開発できることに価値があるみたいにまとめてて、なるほどね、という感じなのですが、じゃあ Elm ってそこらへんどうなってんだっけ? と思い The Elm Architecture やら Elm packages を見直してみました。
Elm はどうなんよ
仮想 DOM を使っています。
先の説明のために Elm の基本構造を簡単に説明します。
すごくシンプルで、
- Model — アプリケーションの状態
- View — 状態を HTML に変換する方法
- Update — メッセージを使って状態を更新する方法
の3つで成り立っています。
画面の入力により Update が走り、Update で Model が更新され、Model の変更は View に反映されます。
Elm のコードは Javascript にコンパイルされますが、コンパイラは全ての関数の引数と返り値の型を解析します。そのため Elm の中ではランタイムエラーは発生しません。また、Elm では
この View の部分が、elm のコアパッケージである elm/html
を引数に受ける形で実装されます。サンプルですが、
pageNotFoundView : Status -> Html.Styled.Html msg
pageNotFoundView httpStatus =
section
[ css
[ position fixed
, width <| pct 100
, height <| vh 100
, textAlign center
]
]
[ div
[ ]
[ h2
[ ]
[ text <|
case httpStatus of
404 ->
"404 Not Found"
_ ->
"hogefuga"
]
]
]
view : Model -> Browser.Document msg
view model =
{ title = "Not Found"
, body =
[ toUnstyled <|
pageNotFoundView model.httpStatus
]
}
elm-css
というパッケージを使用することで CSS もこのように実装できます(話したいことと関係ないのですが、良いので。)。すべて関数で表現されていますが、理解し易いかと思います。基本的に HTML エレメント名の関数が定義されており、第 1 引数のリストに属性を、第 2 引数のリストに子要素を詰める、という形です。
この Html msg
の型を返す pageNotFoundView
たちは、裏で仮想 DOM のデータ構造を作成していると考えることができます。
前段で説明したように、Elm では Model
の変更が起こるたびに view
関数が実行されます。なので、view
関数は引数に Model
を受けます。この Model
と Html msg
から、現在の仮想 DOM と次の Model
の状態をもとにした仮想 DOM を作成、比較し、差分のみを実際の DOM に反映します。
ほーほーなるほどという感じですな。ただ、Elm はそれだけでは終わらねぇ、、ということで、
html.lazy
てやつがいます。こいつは、そもそも仮想 DOM の構築も減らしチャオ、ということが目的の関数です。
lazy : (a -> Html msg) -> a -> Html msg
という関数が elm/html
パッケージには用意されています。この関数は下記のように使用されます。
view : Model -> Browser.Document msg
view model =
{ title = "Not Found"
, body =
[ lazy toUnstyled <|
pageNotFoundView model.httpStatus
]
}
何をしてんねん、ということですが、lazy
は渡された Html msg
の遅延バージョン、「遅延ノード」を作成します。そのノードは、その関数と引数への参照を保持しているだけです。Elm の関数はすべて静的で、同じ入力に対し常に同じ結果を返します。そのため、view
関数は、この lazy ノードを評価する際に、関数とその引数が変更されていないかを確認することで、そもそも仮想 DOM の構築をすっ飛ばします。引数変わってるやん、じゃあ仮想 DOM 作ろか、というかんじなんですね。ほへー、これは Elm ならではやなーという機能でした。
まとめ
lazy ノード、プロジェクトでは使ってません(浅かったので知ったのがだいぶ後半だった)。が、結構ガンガン使ってこーという感じらしいので、必要に応じて今後検討していきたいですな。
Elm はとてもシンプルで、文法のブレも少ないため、誰が書いても似たコードになると思います。ステートの遷移をベースにした UI 構築を勉強する上で、かなりわかりやすく、最初にやるのおすすめなのでは、と思ってます(開発者の Evan さんとしても教育用言語と位置づけているらしい。この記事、結構思想がわかって面白かった、正確性は知らん誰かのブログ)。また、関数型言語の一歩目としても同様の理由で良いのではと思います。
あと、本記事の内容とかは参照のほうがわかりやすいので興味あればみてください。