FRP (Functional Reactive Programming) とは何か

FRPというプログラミング手法について調べたので纏めてみます。


Functional Reactive Programming (以下FRPとも呼ぶ)とは, プログラミング・パラダイム(方法論)の一種です.

例えばObject Oriented Programming (オブジェクト指向プログラミング, 以下OOPとも呼ぶ)なんかと同列の言葉ですね.

OOPによってプログラムを簡潔に書ける場合があるように,
FRPによってある種のプログラムをとても簡潔に記述することができます.

※ここで一応注意しておきたいのは, OOPやFRPというのは, プログラミング言語の性質を表しているわけではなく,
あくまでプログラミングの方法論に過ぎないということです.
たとえばJavaやRubyはオブジェクト指向を言語の核に取り入れていますが, 
言語としてオブジェクト指向をサポートしていないC言語などでも, オブジェクト指向プログラミングを行うことは可能です.

Reactive Programmingとは

FRPとは, 文字通りFunctional(関数的な) Reactive Programmingという意味です.
そこで, まずはReactive Programmingとは何かについて考えてみます.

最初に, 誤解を恐れずにReactive Programmingの本質を一言で説明すると「値そのものではなく, 値をもらう対象を保持するデータ型」を用いるプログラミングである, と言えます.

通常のプログラミングにおいては,

b = a + 1

というのは, 変数aの値に1を足した値を変数bの値とする, という意味を持つ場合が多いと思います.

しかし, Reactive Programmingにおける意味はこれとは異なり,

変数bの値は常に「変数aの値と1を足した値」であることを宣言する, という意味を持ちます.

従って, Reactive Programmingの意味において, 以下の疑似コードの出力は3となります.

a = 1  
b = a + 1  
a = 2  
print b

一見すると奇妙なふるまいに思えるかもしれませんが, 論理回路のassignment(結線)に近い意味であるとも言えます.

f:id:rokugats:20141115144754p:plain

  e = a & b (結線)
  f = c & d (結線)
  g = e & f (結線)
  a = b = c = d = 1 (入力)
  test g == 1 (gが1であることを確認)
  a = 0 (入力の変更)
  test g == 0 (gが0であることを確認)

このように, 依存先となる(ここでは変数a)の変化に応じて反応的に変化が伝搬することから,Reactive(反応的) Programmingと呼ばれます.

さて, 「変数bの値は常に変数aの値と1を足した値であることを宣言する」という操作をもう少し現実的に考えてみましょう.

これは要するに, 変数bの値は「値をもらう対象(変数a)」と「それに1を加えるという手続き」を保持するデータ型であると言えます.

最初に述べた,「値そのものではなく, 値をもらう対象を保持するデータ型」を用いるプログラミングである, とはつまりこのことです.

Reactive Programmingで何が便利になるのか

次にReactive Programmingの用途を簡単に説明します.

Reactive Programmingが用いられている最も身近な例は, Excellなどの表計算ソフトです.

大半の表計算ソフトには関数などと呼ばれる機能があり,ユーザーはセル単位で簡単なプログラミングを行うことができます.

例えば, C1のセルを選択し, 「= B1 + B2」と入力すると, C1のセルの値はB1とB2のセルの値を足した値となります.

また,その後にB1のセルの値を変更すると, それに応じてC1のセルの値も自動的に変更されます.

すなわち, 最初に行った「C1を選択し, = B1 + B2と入力する」という操作は,
「セルC1の値は常にセルB1の値とセルB2の値を足した値であることを宣言する」操作に他なりません.

これはつまり, 表計算ソフトのユーザーは無意識のうちにReactive Programmingを行っているということです.

もしReactive Programmingを用いずに同じことを実現しようとすれば,
「セルB1およびセルB2の値が変更される度にセルC1の値を更新する」プログラムを, ユーザーが明示的に書くことになるでしょう. それではとても面倒ですね.

C言語でReactive Programmingしてみる

上で例として挙げた,

a = 1
b = a + 1
a = 2
print b (3が出力される)

といった極単純なReactive ProgrammingをC言語で実現してみましょう.




とりあえずはmain関数の中だけに注目してください.
main関数の中で行っていることは, コメントに記してある通りで,

52行目から58行目にかけてReactiveな(整数型の)値を宣言し,
60行目から62行目にかけてそれを操作しています.

※ここで少し注意して欲しいのは, 上記のC言語プログラムにおいて, 「Reactive Programmingを行っている」のは,
あくまでmain関数の中においてのみであるということです.

1行目から46行目にかけて記述された処理は, あくまで「Reactive Programmingを行うための舞台セッティング」に過ぎません.

Reactive ProgrammingからFunctional Reactive Programmingへ

上記のC言語によるReactive Programmingは, 次の2つの部分に分けて考えることができます.

  • Reactiveな値を宣言する部分(52行目から58行目)
  • Reactiveな値を命令的に操作する部分(60行目から62行目)

ここまでの知識を用いてFunctional Reactive Programmingを一言で説明すると,「Reactiveな値を宣言する部分のみを用いたReactive Programming」であると言えます.

「Reactiveな値を宣言する部分」だけを用いてReactive Programmingを行い,
実際に意味のあるプログラムを記述することができるのか疑問に思うかもしれません.

ところが, 「Reactive Programmingを行うための舞台」が,
「元となるReactiveな入力値」と「Reactiveな値を最終的に出力する対象」を用意してさえおけば,
意外にも簡単に, というより(普通に)命令的に処理を記述するよりも直感的にプログラムを記述することができます.

例として, 標準入力から文字列を受け取り,その文字列に含まれる矢印キーの個数をカウントして現在の座標を出力するプログラムをFRPによって実現することを考えてみましょう.

(例えば, 「↑↑→→↓」と入力してEnterを押すと, (2, 1)が出力されるようなプログラムです.)

ここで「元となるReactiveな入力値」を

input = 標準入力 :: Reactiveな文字列

「Reactiveな値を最終的に出力する対象」を

output = 標準出力 :: Reactiveな整数のペア

と考えれば, このプログラムは以下の様に定義することができます.

    up    = inputに含まれる, ↑キーに相当する文字の個数
    down  = inputに含まれる, ↓キーに相当する文字の個数
    left  = inputに含まれる, ←キーに相当する文字の個数
    right = inputに含まれる, →キーに相当する文字の個数

    x = right - left
    y = up - down

    output = (x, y)

これをC言語で記述すると, 例えば以下の様になります.



main関数内の処理はあくまでもFRPの裏方であり, FRPを行っているのはFRPCursor関数内です.

FRPCursor関数は副作用を持たない純粋な関数であることが分かります.

これこそが, FRPのFunctional(関数的)たるゆえんとなります.

includeファイルfrp_sample_lib.hは, 本例のために作成したFRPライブラリのヘッダファイルです.
ライブラリ(frp_sample_lib.c)の内容を見たい場合, あるいは上記プログラムを実行してみたい場合は,
こちらからどうぞ.

FRPプログラミング言語

FRPの本質は言語の性質に依存しないということを強調するため,

本記事では典型的な手続き型言語である(ようするに, 普通のプログラミング言語である)C言語を用いてFRPの例を示しましたが,

高階関数型推論などの機能を備えた関数型言語を用いることにより, より強力かつ簡潔にFRPを実現することができます.

(実際にFRPを語る上ではその辺りが一番大事な話であるわけですが, 本記事では扱いません.)

FRPの研究とは

ここまで説明してきたように, FRPの本質は意外と単純です.

言葉通り, FunctionalでReactiveなプログラミングです.

関数型言語Haskellとセットで語られることが多いのも相まって,
アカデミックなイメージが先行しがちですが,

その実体は簡潔かつ直感的にプログラムを記述するための一つの手法に過ぎません.

しかし本質の概念が単純とは言っても, 実際にFunctional Reactive Programmingを行うための舞台を実装しようとすると,

舞台裏の処理にかかる時間だったり, 舞台裏で保持しなければならないデータのメモリ量だったりに起因する,
やっかいな問題が色々と浮上します.

FRPが最初に提案されて以降のFRPの研究の本流は, そういったやっかいな問題を解決, ないしは上手く取り扱うための研究であると言えるでしょう.

おわりに

本記事の目的は「FRPとは何か」を体系的に説明することなので,

醍醐味であるFRPの(フレームワークの)実装手法については触れていません.

またFRPの"旨味"についても本記事ではあまり説明できていないので, そこに焦点を絞った記事も書くかもしれません.

参考

  • What is (functional) reactive programming?

http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631
Stack Overflowにおける質問に対する回答ですが, FRPの本質を説明してくれています.

  • やさしいFunctional reactive programming(概要編)

http://maoe.hatenadiary.jp/entry/20100109/1263059731
筆者がFRPを学ぶ際に最初に参考にさせてもらった記事です.
FRPについて日本語で書かれた記事は少ないので, 大変参考になりました.

  • Elm: Concurrent FRP for Functional GUIs

http://elm-lang.org/papers/concurrent-frp.pdf
Elmという, FRPGUIを扱うことに特化した言語についての論文です.
論文の前半部分においてFRPの過去の主要な研究について纏められていて, 参考になります.
FRPとは何かが分かっていて, 多少なりとも関数型言語の知識があれば読めると思います.

  • Bacon.js

http://baconjs.github.io/
JavaScriptFRPを行うためのライブラリです. Haskell等の知識が不要なので, そこそことっつきやすいです.