トップ «前の日記(2019-04-04) 最新 次の日記(2019-04-10)» 編集

2019-04-06 [長年日記]

_ Reactive Programmingって何?と聞かれたらどう答えたらよいだろうか

問題意識

Reactive Programming って何? Functional Reactive Programming って何? Reactive System と謳っている Vue.js を使って書くことと何が違うの?

って聞かれたどう答える?

React とかって Reactive Programming って言うんでしょ? Reactive って何? Functional Reactive Programming と何が違うの? みたいな展開をとりあえずフロントエンド GUI 方面で考える。

バックエンドのことはとりあえず置いておくし、自分自身は関数型言語と呼ばれるもので何かを作った経験があるわけでもない。が、言葉として存在感が増しているので一度整理しておきたい。

Reactive Programming とはこれだ、という結論を出すことではなくどう答えるか、がポイント。

三行で

  • Reactive Programming というものは存在しないが Fuctional Reactive Programming は存在する
  • Observable という言葉は文脈によって結構意味が違うので注意
  • すべてを Event で考える
  • Promise で先送りしろ

四行だった。

FRPに到る道

今のところ jQuery を卒業して ReactiveX ( Functional Reactive Programming 向けライブラリ ) でコードを書こうと思った場合、いくつかの段階があるなぁということを感じている。

だいたい以下のような感じ。

  1. Reactive に考える
    • 代入変更を自分でモリモリと記述しない
    • jQuery は自分で変更する世界*1、React や Vue は自分で変更しない世界*2
  2. 何が起きているのか?と疑問を感じる
    • 素朴には Observer Pattern というものがある
      • 代入や書き換えではなく method call で実現する
    • Spreadsheet の例はいったんここまででよいと思う
    • Proxy Pattern
  3. Event を中心に考える
    • Queue, Pub/Sub
    • Future Pattern / Promise
  4. Future / Promise のおかげで違いを吸収できることに気づく
  5. 違いが吸収できているならすべてタイミングも長さも不定の Array のような何かで表現可能
    • -> Functional Programming

くらいかなぁ?*3

実際には Timer など非同期のものは全部まとめて扱うことができる(Zone.js っぽい)んだけど、従来のイベントを拾って自分で書き換える、という考え方からの移行を考える分には上のような感じ。

Observer Pattern はイマドキの FRP ライブラリや Reactive なフレームワークを使って書く分には気にする必要がない、少なくとも自分で書く必要はないんだけど、イマドキの「書き方」だけ分かっても応用が効かないと思うのと、少なくとも現時点では ReactiveX の説明には Observer Pattern という言葉は出てくるので知っておいて損はない。実際、Observer Pattern + Proxy Pattern というのは Vue の set を追っていくとなんとなく掴める。React の setState から始まる旅は長すぎるが、Vue 2.5 くらいまでの Vue.set はまだだいぶ分かりやすい。Future も Proxy の一種と捉えることができるので、Observer と Proxy と Queue, Pub/Sub 辺りをなんとなくふーんへーと眺めつつ Promise を書いているうちにちょっとずつつかめてくる気がする。

結構必要な知識が多いので、jQuery 使ってアニメーションできますレベルのコーダーがこれからは Reactive だぜ!って盛り上がるとキツイと思う。どっちかというと Container / Presentational Component の分離を意識して Presentation の方に注力した方が成果は出やすいはず。

Container からロジック、FRP をモリモリやっていける人を見つけて組むことができれば面白いものを作れるようになると思う。少なくともなんでもかんでもイベントとして定義して emit できるようになっていれば、それを Container 側で Composite することが可能になる。自分でモリモリ書き換える系の実装を続けているとそれは難しくなってしまう。

言い方を変えると イベントの定義がロジック側とプレゼンテーション側のインターフェイスになる のだ。これが分かっていないとつらいコードから抜けられない。まずはここからだと思う。

cf.

Observableとは何か

これは実装によって意味が大きく異なるので文脈の確認が超大事。

  • 例えば MobX で Observable と言ったら変更を監視したい値のこと
    • 古典的な Observer Pattern で言う Subject に近い、はず
  • ReactiveX で Observable と言ったら Event など Stream を流れてくる「何か」
    • これを pipe で処理してから subscribe する

Reactive Programmingとは何か?

分かんない。

いろいろ調べたけど、Functional Reactive Programming を Reactive Programming と Functional Programming に分解して考えるのは無理な気がしている。つまり、Reactive Programming というものは存在しなくて、すべて FRP の話をしている。譲って「Reactive に考えましょう」までが Reactive Programming で、考え方止まり。

個人的には Reactive Programming と呼ぶのは名前付けに失敗している気がする。Event Stream Programming でよいと思うのだが、Stream というのは名前空間的に非常に厳しい戦いになってしまう*4ので、もう FRP でいいよという気持ち。考え方はともかく、少なくとも書いてる分には Reactive という言葉にはあんまり意味は感じないし、言葉の意味としては Stream も Observable も一意には決まらないので、個々の言葉から意味を掘り下げていくのは無理。分解するのではなく合成したまま Functional Reactive Programming として丸呑みせざるを得ない感じ。

オブジェクト指向は間違いなくオブジェクトと対話してるし、関数型は関数を書いているじゃんすか。なのに Reactive を書いているわけじゃない、ってのがすごくしっくりこないのですよ。とりあえず名前としてね。Functional の部分は多少感じるけど。

結論、Reactive Programming は存在しないが Functional Reactive Programming は存在する。中途半端に省略して Reactive Programming と呼ぶと混乱が増す。

ではFRPとは何か

Promise, async/await が非同期を同期的であるかのように「呼べる」ところまでで留まっているのに対し、その集合を集合として扱えるように Event Stream として概念を整理したのが Functional Reactive Programming なのではないかと今のところ自分は思っている。

そしてそこで果たす Promise の役割は大きいのでやはりしっかり Promise はつかんでおきましょうという気持ちになっている。Promise は起きたイベントをすぐに処理しなくてもよい状態を実現してくれる。おまじないとしてでなく、ちゃんと自由に使えると強い。

※ 蛇足だけど FRP は別に新しいパラダイムではないと思う。オブジェクト指向と関数型のいいとこどりをしようとする考え方の一つでしかなく、イディオムと考えるくらいでよいと思う。

FRPの嬉しみとは何か

個人的には FRP の簡単な例として RxDart のコナミコマンドの例が最高だと思っている。

ReactiveX/rxdart: The Reactive Extensions for Dart

イベントを一つずつしか扱えない状態でこれを実現しようと思うとしっかりと状態を保持して、都度都度、一致しているかどうかをチェックすることになってしまうが、FRP なら「10回の keyup をまとめて取得できるように整える」ところから始まり、10回の keyup の内容をそのままコナミコマンドのシーケンスと比較して一致してるかどうか見ればよいだけ、になってしまう。

RxJS で同じものを作ってみたが、IterableEquality みたいな便利な API を見つけることができなかったので take(10) と combineAll() で処理しようとしたら Promise にしろって怒られてしまった。そこで「あーなるほど Promise か!」って初めて後回しにできるメリット強く感じることができましたとさ。これは面白い!

今後

個人的には、昨今はインフラが非同期分散を前提にしてたりするのでバックエンドについても FRP の考え方を取り入れた方が無理のないコードに落とし込みやすくなるんじゃないかなぁということを考えている。もしかしたら自分としてはバックエンドの方が恩恵は大きいかもしれない。(とは言え同期的な考え方と retry で十分な部分まで reactive にしようなんてことは思わないけど。)

参考

Tags: JavaScript

*1 class名書き換えたりね

*2 変化した値が自動的にセットされる、適用される

*3 Cycle.jsの場合はもう少し違っていて、外部の世界とやりとりしてside effectを生むDriverとpure functionに分離するというターンが入る。reactiveというよりは、よりfunctionalな感じがする。

*4 Stream API というものもあるので。こっちの方がより直感的な「Data の Stream」なので FRP の文脈で Data と言っちゃうのは本当によくないと思う。