37
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Elm2Advent Calendar 2019

Day 16

図解 The Elm Architecture の流れ

Last updated at Posted at 2019-12-15

はじめに

The Elm Architecture は、コードの再利用性・テストのしやすさに優れた設計パターンです。
しかし、Elm に慣れていない人にとっては、The Elm Architecture がどのような流れで実行されていくのか、想像しづらいかもしれません。

本記事では、 The Elm Architecture の処理の流れを、Elm Guide(日本語訳版) の例に沿って説明していきます。

先に以下の記事で、The Elm Architecture を触っておくとより理解が進むかもしれません
フワッとわかった気になるElm入門

The Elm Architecture の処理の流れ

TEA.png

上記の図だけではわかりにくいので、Elm Guide ボタンの例に沿って具体的に説明していきます。

例: ボタン

ボタンとカウンターに 0 と画面に表示されてから、ユーザーが [+] ボタンを押し、カウンターが 1 に更新されるまでの処理の流れを見ていきましょう。
参考: Elm Guide ボタン

Elm・Runtime System・ユーザー

TEA_Elm_RuntimeSystem_User.png

流れを確認する前に、図の 「Elm・Runtime System・ユーザー」について、軽く説明をしたいと思います。

まず、「ユーザー」とは、例えばあなたのことです。
あなたというユーザーは、ブラウザを通して Web ページを閲覧して、ボタンをクリックしたりテキストボックスに入力することができます。

次に、「Elm」は Web ページの設計者です。
Web ページの内容や、ユーザーから操作があったときにどういう画面の変化が起こるか、を決めた設計書を作るのが Elm のお仕事なのです!

それでは「Runtime System」は何をするのものなのでしょうか?
Runtime System は Elm とユーザーの仲介者のようなものです。

「ユーザー」が Web ページを見たりクリックしたりするのに対し、「Elm」は Web ページの内容や、ユーザーからの操作に対する画面の変化を決めた設計書を作る、という役割があります。
「Elm」と「ユーザー」の間に「Runtime System」がいて、「Elm」からの設計書通りに Web ページを「ユーザー」に表示して、「ユーザー」から操作があれば「Elm」にメッセージを送るということを行うのです。

ちょっと難しいかもしれませんが、ゆっくりと慣れていきましょう!

Model の初期化

TEA_ex1.png

さて、それでは流れを確認していきましょう!

まず1.の **Model とは、「アプリケーションの状態」**のことです。
少しわかりづらいかもしれませんが、今回の例では、Model は「カウンターの状態」を意味します。
そして init 関数は、「カウンターの状態」である Model を 0 という値で「初期化する」ものです。
「初期化する」という言葉の意味どおり、init 関数は Elm が起動してから最初に一度だけ呼び出されます

次に view 関数ですね。
view 関数は、Elm の「Web ページの設計書」を作る部分です。

しっかりとした言葉で説明すると view 関数は、Model の値を元に、「Web ページの設計書」である Html Msg を構築するものです。

具体的に考えていきましょう。
今回の例で、view 関数は「カウンターの状態」である Model の 0 という値を元に、「カウンターが 0 の Web ページの設計書」である Html Msg を構築している、と考えることができます。

次に2.で Elm から「Web ページの設計書」である Html Msg を Runtime System に送ります。
最後に 3.で Html Msg を受け取った Runtime System は、Html Msg から実際の「Web ページ」を生成してブラウザに表示します。
もっと具体的に言うと、「カウンターの Web ページの設計書」である Html Msg を受け取った Runtime System が「カウンターの Web ページ」を生成してブラウザに表示する、という流れになります。

TEA_ex1.png

それでは、最後にもう一度全体の流れを追っていきましょう。

  1. Elm の init 関数で「カウンターの状態」である Model を 0 に「初期化する」
  2. Elm の view 関数で Model から 「カウンターが 0 の Web ページの設計書」である Html Msg を構築して Runtime System に渡す
  3. Runtime System で Html Msg から Web ページを作成し、ブラウザに「カウンターが 0 の Web ページ」が表示される

これで全部ですね!

Web ページの表示と、 Msg の送信

TEA_ex3.png

さて、4.からの流れを見ていくその前に……Html Msg についてまだ説明が足りていませんでした。

今までの説明だと、Web ページをブラウザに表示するまでは良いのですが、その後ユーザーから操作を受けたときに Runtime System が反応することができません。
カウンターのページが表示されてから、ユーザーが[+]ボタンをクリックしてもカウントが増えず、何も変化が起きないということです。(えぇ〜)

この後説明しますが、「カウントを1増やす」処理は Elm 側で行われます
なので、Runtime System から Elm へ、ユーザーが[+]ボタンをクリックしたらカウントを1増やす、という「メッセージ」を送る必要があるのです!

メッセージ, Message, Msg......!!
そうです、Msg です!
Html Msg の Msg とは、ユーザーからの入力を受けたときに Runtime System から Elm へ送る「メッセージ」だったのです。
そして Html Msg は、「ユーザーから入力があったら Elm へ Msg を返す、という指示を含んだ Web ページの設計書」なのです!

今回の例では、 Html Msg は「ユーザーから[+]ボタンを押されたら Increment という Msg を返す、という指示を含んだ、カウンター 0 の Web ページの設計書」を意味しています。(長すぎるよ〜)

まとめてみましょう。
まず2.で Elm が、こんな Web ページを作って欲しい、こんな入力があったら Msg を返してほしい、という指示を Html Msg に含めて Runtime System に送ります。
次に3.4.で、Html Msg を受け取った Runtime System が実際にページ生成して表示し、ユーザーから入力を受け取ります。
最後に5.で、 Runtime System が、ユーザーの入力に対応する Msg を Elm へ送ります。

TEA_ElmToRuntimeSystem.png

Elm で Model の更新と、Html Msg の構築までの流れ

TEA_ex2.png

さて Html Msg が明らかになったところで、4.と5.の流れから見ていきましょう。
ユーザーが[+]ボタンをクリックして、Msg として Increment メッセージが Elm に送られ、 update 関数に渡されます。

update 関数は、受け取った Msg と、以前の Model の値から、新しい Model の値を生成するものです。
update 関数には、以前の Model の値から 新しい Model の値を生成する処理が複数あり、受け取った Msg の種類で複数の処理から一つの処理を決めます。
実際に6.を確認してみましょう。
update 関数には、「カウントを1増やす処理」と「カウントを1減らす処理」があるのですが、今回は Increment メッセージを受け取ったので、「カウントを1増やす処理」が実行され、 以前の Model の値である 0 から 新しい Model の値である 1 が生成されました。

7.と8.については、もう大丈夫ですよね!
7.の view 関数は、 Model の値である 1 から 「ユーザーから入力があったら Msg を返す、という指示を含んだ、カウンターが 1 である Web ページの設計書」である Html Msg を Runtime System に渡します。
8.では Runtime System が受け取った Html Msg を元に「カウンターが 1 である Web ページ」を作成、表示しています。

お疲れ様でした!

流れの説明

さて、具体的な例を見たところで、再び The Elm Architecture の流れを確認していきましょう。

TEA.png

  1. init 関数で Model が初期化される
  2. view 関数で Model から Html Msg を作り、Html Msg がブラウザ上の Runtime System に渡される
  3. ブラウザに、 Html ページが表示され、ユーザーからの操作を待つ
  4. ユーザーからの操作があった
  5. Runtime System から、ユーザーからの操作に対する Msg が update 関数に渡される
  6. update 関数で、前の Model と 渡された Msg から、新しい Model を作り、新しい Model が view 関数に渡される

この後は、2.〜6.が繰り返されます。

重要なのは、初期化されてページが表示された後

  • ユーザーから入力を待つ
  • 入力があると、 Runtime System から update 関数 へ Msg が送信される
  • update 関数で、受け取った Msg と前の Model から、新しい Model が作られる
  • view 関数で新しい Model から Html Msg を作りブラウザにページを表示する

という一連の流れが繰り返されるということです。

余談

Command

今回は、Browser.sandbox のみで、Command と Subscription については扱いませんでしたが、少しだけヒントを書いておきたいと思います。

まず、Command は、 Elm でランダムな値や、時間の取得、HTTP リクエストの発行、などを扱うための方法です。

「え? Elm ってランダムな値とか、時間って、標準の関数から取れないの?」と、不思議に思う方もいるかもしれません。
そうなんです!
これは、Elm では「関数は同じ値を入れると、どんなときも同じ値を返さなければならない」という制約があるからなのです!!(参照透過性(Referential transparency) と呼ばれる制約です)
いったい、どういうことでしょう?

まずは単純な例として、整数に1を足した数を返す plusOne 関数を考えてみましょう。

plusOne関数
plusOne : Int -> Int

plusOne 3 -- = 4
-- ****いろいろな処理****
plusOne 3 -- = 4

この plusOne 関数は、3 という値を入れると、どんなときも同じ 4 という値を返します。
当たり前ですよね?

さて、それでは仮に、整数のランダムな値を得る getRandom 関数を考えてみましょう。

getRandom関数
getRandom : () -> Int

getRandom () -- 15
-- ****いろいろな処理****
getRandom () -- 4

ああ!! getRandom 関数は、() という値を入れても、同じ値を返してくれないようです……

実は、getRandom 関数のような**「同じ値を入れると、どんなときも同じ値を返さなければならない」という制約を満たさない関数は、 Elm ではどうやっても実装することができません。**(試しに getRandom 関数を実装してみるといいですよ!)

時間を得る関数も同様に、「同じ値を入れると、どんなときも同じ値を返さなければならない」という制約を満たしません。(もしそんな関数があったら、時間が止まっていることになります!)
では、Elm ではどのようにして、ランダムな値や、時間を得るのでしょうか?

ここで Command の登場です!

例えば、Elm が直接ランダムな値を得るのではなく、 Command を使って Runtime System に「ランダムな値を得たら、その値を Msg で返してほしい」と指示して、ランダムな値を取ってきてもらうのです。

すなわち **Cmd Msg は、Elm から Runtime System への「コマンド(ランダムな値を得る、時間を得る、など)を行ったら、Msg (定まったランダム値、時間) を返す、という指示書」**なのです。

より詳しい説明と例が、 Elm Guide コマンドとサブスクリプションに書かれているので読んでみてください。

「Elm 上ではランダムな値を得る関数が作れないから Runtime System に指示する、だなんて回りくどい」と思うかもしれませんが、この「関数は同じ値を入れると、どんなときも同じ値を返さなければならない」という制約は、テストの際にとても素晴らしい恩恵があるのです。
テストでの恩恵について、より詳しい説明は、Elmで副作用を扱う仕組みCmdがとっても良い理由に書かれているので、ぜひ読んでみてください!

Cmd Msg と Html Msg

ついでに、もう一つ Cmd Msg についてのヒントを載せておきたいと思います。

Cmd Msg は、**Elm から Runtime System への「コマンド(ランダムな値を得る、時間を得る、など)を行ったら、Msg (定まったランダム値、時間) を返す、という指示書」**でした。

さて、Cmd Msg と Html Msg 、とても似た形をしていると思いませんか?

そういえば Cmd Msg は、 Elm の「関数は同じ値を入れると、どんなときも同じ値を返さなければならない」という制約を回避するためのものでしたが、ユーザーの入力は、どんなときも同じ値になるとは限らないですよね……

おわりに

本記事では The Elm Architecture の処理の流れを、Elm Guide(日本語訳版) の例に沿って説明しました。
また余談では、 Command と Subscription のヒントのために、Elm には「関数は同じ値を入れると、どんなときも同じ値を返さなければならない」という制約があることや、Cmd Msg はその制約を回避するために Elm から Runtime System への「コマンドを行ったら、Msg を返す、という指示書」である、ということも説明しました。
(本当は図をいれて、Browser.elementの流れまで説明したかったけど疲れた)

The Elm Architecture を理解して、最強のオレオレ Web アプリケーションを作りましょう!!
それでは、良い Elm ライフを!!


説明文に誤り、誤字脱字などがありましたら、ぜひ編集リクエストやコメントなどでお知らせください!
(図がガバガバなのは、許して……)

37
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?