Blog
Principles of Reactive Programmingコースに参加している話 Week.2
こんにちは、15卒のフィです。
前回、Courseraの「Principles of Reactive Programming」コースの第一回を紹介させていただきました。
Principles of Reactive Programmingコースに参加している話 Week.1
今回はWeek 2の内容について紹介させていただきます。
Week 2で勉強した内容は主に以下の三つです。
- 関数と状態(State)管理
- 状態管理するためのテクニック:ObserverパターンとFRP(Functional Reactive Programming)スタイル
- 簡単なFRP実装
1. 関数と状態管理
プログラミングの中に、時間に変化されないもの(例えば:1+1=2のような計算)は管理が凄く簡単で、副作用もありません。ですが、複雑なプログラムではそんな簡単な処理だけではいけない。複雑なプログラムでは、時変 (time varying) の概念が存在します。例えば、銀行システムの残高を例にすると、ある特定の一時点では、常に一つの残高に定まります。時変を変数で扱う場合、状態の管理が必要になります。
オブジェクトに状態が存在するというのは「そのオブジェクトの振る舞いが自分の前の操作に影響される」と定義されます。オブジェクトの状態を管理するために
- オブジェクトが変更されるヒストリー
- オブジェクトが変更される瞬間、そのオブジェクトに依存する全てのものに通知する
の二つのことを管理する必要があります。
Scalaは関数型言語ですが可変関数をサポートするために、val
の代わりにvar
が使えます。つまりvar
を使えば可変状態を実装することもできます。var
の使い方ですが、基本的にJavaと同じく、var
の変数を代入すればよいですね。
1 2 3 |
var x = 1 x = 2 println(x) |
ですが、代入をすることで一つの問題が発生します。それは、二つの変数が等しいことを判断するのが難しくなることです。その問題から「参照透過性」という概念が生まれます。その概念は関数型言語の基本の概念として、説明するのが簡単です:同じ関数であれば、いつ実行しても結果が同じであること。
1 2 |
val x = E; val y = E //Eは任意な表現です val x = E; val y = x //参照透過性をもつとこのコードは上のコードは同じのことです |
まとめると、状態管理をする場合、変数 (var) を使う必要があります。ただし、変数 (var) を使うことにより、変数間が等しいことを確認することが難しくなります。そこで、もっと簡単に状態管理ができる方法はあるの?と疑問を持ちつつ、次のレクチャーに進みました。
2. Observerパターン
上記の例で、状態管理がややこしく感じます。状態を別の観点から考えると「イベント」として認識できます。「状態が変更」されることは、「イベントが発火する」と考えることもできます。イベントを管理するために、レクチャーではObserverパターンとFRPの技術について学びました。
Observerパターンはデスクトップアプリプログラミングからウェブプログラミングまで幅広く使われている技術です。Observerパータンの別名として
- Pub/subモデル
- MVCモデル
がよく知られています。Observerパターンの実装は基本的に以下のSubscriberとPublisherインターフェースで実装されます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
trait Publisher { private var subscribers: Set[Subscriber] = Set def subscribe(subscriber: Subscriber): Unit = subscribers += subscriber def unsubscribe(subscriber: Subscriber): Unit = subscriber -= subscriber def publish(): Unit = subscribers.foreach(_.handler(this)) } trait Subscriber { def handler(pub: Publisher) } |
Publisherは状態管理をする役割で、妥当なタイミングでpublish()すれば、Subscriberに実装された処理が実装される、というのが基本的な考え方です。
Observerパターンの実装は簡単ですし分かりやすいのですが欠点もあります
- 命令型プログラミングスタイル(publish()などの呼び出しは人間で判断しかできない)
- 並列プログラミングでは不向き
- 一つのビューに対して一つの状態でしか表せない
そこでイベントハンドルスタイルのプログラミングの欠点を証明するため、Martin先生は面白い数字を例としました:Adobeのアプリケーションでは1/3のコード量ではイベントハンドリングのためのものですが、Adobeの1/2のバグはそのコードから生まれたそうです。
そこで別のプログラミングスタイル:Functional Reactive Programming (FRP)で、状態管理をもうちょっと分かりやすくなります
3.FRP(Functional Reactive Programming)
Functional Reactive Programming は1997年の論文で定義されました。その論文を興味がある方は是非見てみてください:http://conal.net/papers/icfp97/icfp97.pdf
論文ではイベントストリムのモデルについて紹介、そこで「イベントの発見」と「定期的の間隔でサンプリング・解析」するのを簡単にできるプログラミングスタイルを提案しました。
レクチャーの中では、元の論文とちょっと違うScala.Reactというモデルを使いました。そのモデルは Deprecating the Observer Pattern の論文で紹介されました。
レクチャーで紹介されたScala.Reactのモデルは
- Signal
- Var (Signalの拡張クラス)
という二つの概念で構成されました。Signalは「状態を表す」となり、Varは「Signalの値」を表すものです。Signalは信号のシグナルと同じく、時変のもの考えて頂ければよいです。Signalの中の処理も呼び出されている毎に毎回再評価されるのがポイントです。
Signalのアイディアを表すための簡単な例です:
1 2 3 4 5 6 7 8 9 10 11 |
var a = 2 var b = 2 * a a = 3 //bが更新されない var a = Var() var b = Var() a() = 2 b() = 2 * a() a() = 3 //bが更新される |
つまり、「代入」という概念が「シグナルの更新」という概念にシフトしました。
レクチャーの中にFRPのパッケージの実装も簡単に紹介されました。また、FRPを用いて開発したい方は Li Haoliさんの便利なライブラリがありますので:https://github.com/lihaoyi/Scala.rx、ご興味のある方は使ってみてください。
4. 宿題+まとめ
Week. 2の宿題はScalajsを使って簡単なReactiveウェブシステムを作る宿題です。内容はSignal()とVar()の使い方ぐらいが理解できればすぐ解けるレベルです。(Signal()のVar()の深く理解しなくても大丈夫です)。Scalajsを使いますので、レクチャーの内容以外のScalajsの初心者知識も獲得できます、おすすめです。
Week. 2では状態・シグナル・Reactiveなどの最近流行っているキーワードをたくさん学べました。
Reactiveについて調べたい方、リッチなUIのシステムを作っている方などおすすめですので、是非受講してみてください。
Author