この記事は Elm Advent Calendar 2016 11日目の記事です。
まえがき
ElmはかつてFunctional Reactive Programmingの本命と言われていました。しかし、ElmはFRPと袂を分かってしまいます。A Farewell to FRP という記事にその内容が記されています。
日本語訳もこちらに出ているので、ぜひ目を通してみてください。
しかし、Elm を学んで幾つかアプリを作ってみるという経験を通して、 Elm(Elmアーキテクチャ)の本質はFRPであると強く思うようになりました。
Functional Reactive Programming とは
FRPには決定的な説明はなく、様々な形でその概念を説明しようという試みがなされています。例えば、Wikipediaには
Functional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous data-flow programming) using the building blocks of functional programming (e.g. map, reduce, filter)
とあり、Haskell Wikiには
Functional Reactive Programming (FRP) integrates time flow and compositional events into functional programming.
という説明があります。
この2つの説明に共通しているのは、「FRPとは非同期的なデータストリームを扱うプログラミングパラダイムである」という点ではないかと思われます。
では、その「データストリーム」とは一体何なのでしょうか。以下、非同期的なデータストリームについて触れた上で、その次にElmアーキテクチャとの関連を考えてみたいと思います。
非同期的なデータストリーム
FRPという概念が最初に提唱されたのは1997年のFunctional Reactive Animationという論文です。ユーザーからのインタラクティブな操作を関数として操作するため、BehaviorとEventという第一級の値が考えられました。その定義はだいたい次のようになります。
$Time \equiv \left\{ t \in R | t > 0 \right\}$
$Behavior \ a \equiv Time \rightarrow a$
$Event \ a \equiv List(Time \times a)$
Timeとは時間を表し、Behaviorは時間の関数、そしてEventは時間とペアになった無限リスト(すなわちストリーム)と考えられます。これから、非同期的なデータストリームとは、時間と値がペアになった無限リストであるということができます。
Elmアーキテクチャ
Elmアーキテクチャはリアクティブなアプリケーションを作成する上で、我々にとって使いやすいパラダイムを提示してくれました。
FRPを全面に出していた以前よりもとっつきやすく、それぞれの要素を意識してプログラミングすれば良いので、より書きやすくなったのではないかと思います。
しかし、ElmアーキテクチャはFRPを知ると、よりアーキテクチャ自体の理解が進むのではないのかと思います。非同期的なデータフロープログラミングであるということを念頭に置くだけでも、Elmアーキテクチャのそれぞれの要素の相互関係がわかりやすくなるのではないでしょうか。
まず簡単にElmアーキテクチャについて解説します。
次に、ElmアーキテクチャとFRPを結びつけて捉えてみます。Elmアーキテクチャを構成するのに重要な__Model__ 、Update と Subscription を中心に考察します。
Elmアーキテクチャの簡単な説明
Messages
アプリケーションを操作させるためのトリガーとなるもので、 Updateでハンドリングします。
Model
Modelはアプリケーションの状態を表しており、その中でもinitは初期状態を表しています。
Update
Messagesを受け取って新しいModelを返します。
場合によっては新しいCommandを発行します。
Command
外に出す命令、コマンドのことで、DBの検索やTaskを実行します。
View
HTMLとして状態を表現します。
Subscription
マウスイベント、キーボードイベントや時間などの外部イベントをリッスンさせるための機構です。
Main
アプリケーションを実行します。
ElmアーキテクチャとFRP
Model と Update
Elmアーキテクチャでいうところの Model は、FRPとして解釈するとデータストリームに相当します。Elmの公式チュートリアルには
Model — the state of your application
とあります。Elm アーキテクチャでは Model を状態と呼んでいますが、これは初心者であったときの私にとってはややわかりづらい説明でした。なぜなら、状態があるならそれはミュータブルな世界があって、それを破壊的に変更できる、という先入観があったからです。しかし、Elm アーキテクチャの Model と Update のシグネチャーを見てみましょう。
model : Model
Model については説明は不要だと思います。
update : Msg -> Model -> Model
Update については、メッセージと Model を順番に受け取って、新しい Model を返すというように読むことができます。したがって、状態を更新する、という説明はややわかりづらいのではないかと思われます。ElmアーキテクチャをFRPの延長として捉えるのであれば、Update は Model というデータストリームをハンドリングし、新しい Model を返す関数と捉えるのがいいのではないでしょうか。
Subscription
さらに、データストリームとして捉えるという方法は、Subscription の挙動を理解する上で役に立つのではないでしょうか。
シグネチャーを見てみましょう。
subscriptions : Model -> Sub Msg
Model を受け取り、Sub Msg を返します。クリックイベントやマウスイベントなどの外部イベントは、そもそも Model には組み込まれていません。したがって、Subscription は_無からストリームを作っている_と見て取れます。そして、それを Update でハンドリングし、新しいストリームを返すと考えられます。
View
View のシグネチャーを見てみましょう。
view : Model -> Html Msg
View はストリームの任意の要素をレンダリングする存在そして捉えられます。無限リストを受け取って、ある時刻 $t$ の要素をHTMLに変換し我々に提示します。
Elmアーキテクチャにおけるデータフロー
以上の説明を簡単な図にすると、次のようになるかと思います。無限リストの中の一つのセルにおけるデータの流れを図にしてみました。以下のような図が無限に連なっていると考えてください。
ModelがUpdateによって次々と更新され、途中SubscriptionをUpdateが受け取ってさらに新しいModelを構築しながら、それが最終的にViewによってレンダリングされます。