この記事について
この記事は、Conquering ReactiveSwift: Introductionの翻訳です。
翻訳しても問題ないか著者さんにTwitterで聞いてみたところ快く了承していただきました。
Hey Takanori, I am so glad that you found this useful. Of course, you can translate and share.
— Susmita Horrow (@SusmitaHorrow) October 4, 2018
記事本文中にもありますが、この記事は全6回シリーズの最初の記事です。次回以降の記事も翻訳して投稿する予定です。
以下本文です。
ReactiveSwiftを克服する: 導入
リアクティブプログラミングの学習曲線は急峻だ
私が関数型リアクティブプログラミング(Functional Reactive Programing, FRP)を学習していたとき、よくこの言葉を耳にしました。命令型プログラミング畑出身だった私は、FRPのいろいろな概念を理解するために奮闘しました。**FRPの最も重要な利点は、時間の経過のモデル化が容易になるところです。**しかし、これは簡単には理解できないこともあります。みなさんが学習の最初の一歩を踏み出す助けになればと思い、「ReactiveSwiftを克服する」と題したシリーズで私の理解を共有します。ReactiveSwiftについて初心者にもわかるように少しずつ案内することを目標としています。
この記事はシリーズの最初の記事です。ここでは、関数型リアクティブプログラミングについて紹介し、命令型プログラミングとの違いを明らかにします。
命令型プログラミング vs 関数型リアクティブプログラミング
この2つの枠組みの違いを理解するために、次のようなUIを考えてみましょう。UILabel(label
と呼びましょう)とUITextView(textView
と呼びましょう)があって、label
にはtextView
に入力されたテキストが反映されるとします。この振る舞いを実装するには、こう書きます:
func textViewDidChange(_ textView: UITextView) {
label.text = textView.text
}
上のコードはちゃんと動きます。私達はずっとこうやってきました。なにが問題でしょうか?もう少し考えてみましょう。label
のテキストを更新するための文はこうです:
label.text = textView.text
これは代入する文です。
これの意味はなんでしょうか?これは、代入の瞬間においてlabel.text
がtextView.text
と等しくなることを意味します。この文は代入の前後のlabel.text
の値については何も関知しません。つまり、*「 label.text
はtextView.label
と等しい 」*という命題は、時間の経過を考慮に入れると、必ずしも真であるとは言えなくなるのです。
したがって、私たちはこの文をtextViewDidChange
というデリゲートメソッドの中に書くことでlabel.text
の値を維持します。命令型プログラミングではこのような関連性をエレガントに書くのは非常に難しいです。
リアクティブバインディングへようこそ
リアクティブプログラミング( とりわけ、 ReactiveSwift)では、バインディングでこの問題を解決することができます。
label.reactive.text <~ textView.reactive.continuousTextValues
この文はlabel
のテキストがtextView
のテキストとlabel
が存在している間ずっと等しくなることを示します。
目新しいものに気づきましたか? <~
はバインディング演算子と呼ばれるものです。演算子の左辺にあるのはバインディングターゲットで、右辺にあるのはバインディングソースと呼びます。「ReactiveSwiftを克服する」シリーズでは以降の記事でこの文を深掘りします。
命令型プログラミングとリアクティブプログラミングで"hello"を入力した時の違いを図にして考えてみましょう。
命令型のやり方では、変わりゆく状態という観点からシステムを設計します。たとえば、上の図では、それぞれの文字の入力を関知して、その都度label
の状態を更新しています。
FRPでは、システムをイベントのストリーム(流れ)に応じた振る舞いという観点から設計します。上記の例では、テキストの入力というストリームに応じたlabel
の振る舞いを定義しています。テキスト入力という個々のイベントに対する状態の更新について関知していません。
リアクティブプログラミングのメリットは他にもあります。それはlabel
の振る舞いが宣言の時点で明確になっていることです。対して、命令型プログラミングでは制御ロジックがあちこちのデリゲートメソッドに散らばってしまいます。長い目で見ると、振る舞いの理解が難しくなっていきバグの起こりやすいコードになりかねません。
便利な用語
関数型HaskellプログラマのHeinrich Apfelmusは、上で議論してきた考え方を二つの文に要約しています。
- 意味論的には、FRPは、変化していく状態の代わりに時間変化する関数を使ってシステムを記述します。
- 統語論的には、FRPは、宣言の時点で値の動的な振る舞いを完全に決定します。
次回は、、、
この記事でFRPに対するモチベーションが湧いたでしょうか?サンプルコードはこちらからダウンロードすることができます。次の記事では、ReactiveSwiftの基本について議論します。