Last active
August 29, 2015 14:13
-
-
Save ken-okabe/9ea4ac850c71210a3108 to your computer and use it in GitHub Desktop.
数学的FRP解説からのFacebook-React
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
##リアクティブの説明 | |
もっとも身近でわかりやすい説明とは、以下のようなものです。 | |
車輪の再発明をする必要は感じられないので、 | |
@hirokidaichi 氏による解説 | |
[2015年に備えて知っておきたいリアクティブアーキテクチャの潮流](http://qiita.com/hirokidaichi/items/9c1d862099c2e12f5b0f) | |
から引用させていただきます。 | |
>## エクセルとリアクティブプログラミング | |
リアクティブプログラミングについて、説明するときに「Excel」みたいなものだよとよく言われます。以下のシートをみてください。(=は抜いてあります。) | |
 | |
>>このようなシートがあったときに、A1の値を書き換えたらどのように挙動するでしょうか。 | |
 | |
>セルの各値は、再計算を指示することなく、値同士の関係性のみを記述しただけで自動的に再更新されます。 | |
>次に既存のプログラミングパラダイムで、値同士の関係を記述した場合を考えてみましょう。 | |
``` | |
var A1 = 1; | |
var B1 = A1 + 1; | |
var C1 = B1 + 1; | |
var A2 = sum(A1,B1,C1); | |
var B2 = A2 * 2; | |
``` | |
>このような処理があったときに | |
``` | |
A1 = 2; | |
``` | |
>としても、B1,C1,A2,B2の値は変化せず、A1のみが変化します。 | |
既存のプログラミングパラダイムでA1とB1の関係を記述する場合、observerパターンなどを使い、次のように書く必要があるでしょう。 | |
``` | |
var A1 = new Cell; | |
var B1 = new Cell; | |
A1.on("change",function(e){ | |
B1.setValue(e.value + 1); | |
}); | |
``` | |
>リアクティブプログラミングを言い換えるのであれば、Observerパターンを意識させず関係性のみを記述することともいえるでしょう。 | |
>このようなパラダイムの言語として代表的なものはハードウェア記述言語であるVerilogなどが挙げられるでしょう。電子回路のような物理的パスと信号を取り扱うシステムは、データフローとイベントを取り扱うアーキテクチャと極めて親和性があります。 | |
##表計算とリアクティブ | |
「Excel」という**表計算アプリ**の例が、 | |
**リアクティブプログラミングの概念**を説明するときに | |
よく用いられるというのは、何気に本質的で、 | |
表計算なんていう**「論理」を網羅するようなアプリケーション** | |
と**リアクティブは非常に親和性が高い**、ということです。 | |
##もしもJavaScriptがリアクティブだったら? | |
```js | |
var a, b; //Excelのマス目に相当する | |
a = 1; //aのマス目の値は 1 | |
b = a * 5; //bのマス目の値は、aのマス目の値の5倍と等しい | |
a = 2; //aのマス目の値を 2に変更 | |
console.log(b); // bのマス目はリアクティブに再計算され | |
//10 が表示される!! | |
``` | |
こうなるはずです。 | |
**表計算**っていうのは、さっき書いたように | |
**「論理」** | |
を網羅するアプリケーションですが、上のコードで、 | |
その**「論理」をリアクティブな特徴として強烈に体現している行** | |
がたったひとつだけありますね? | |
```js | |
b = a * 5; //bのマス目の値は、aのマス目の値の5倍と等しい | |
``` | |
もちろんこれです。 | |
これは表計算アプリのマス目に宣言されたとおりの | |
左辺と右辺の値が常に等しい**「数学の方程式」**なんです。 | |
つまり**純粋な「論理」**です。 | |
これが、リアクティブプログラミングが | |
関数型プログラミングと非常に相性が良ろしく、 | |
関数型リアクティブプログラミング(Functional Reactive Programming) | |
と統合(フュージョン)されてしまう最大の理由です。 | |
##破壊的代入による論理破綻と時間変化 | |
ただし、 | |
```js | |
var a, b; //Excelのマス目に相当する | |
a = 1; //aのマス目の値は 1 | |
b = a * 5; //bのマス目の値は、aのマス目の値の5倍と等しい | |
a = 2; //aのマス目の値を 2に変更 | |
console.log(b); // bのマス目はリアクティブに再計算され | |
//10 が表示される!! | |
``` | |
このコードで「論理」もへったくれもない行がひとつだけありますね。 | |
``` | |
a = 2; //aのマス目の値を 2に変更 | |
``` | |
こいつです。 | |
数学の方程式だ、論理だ、というのであれば、 | |
$$ | |
a = 1 | |
$$ | |
と | |
$$ | |
a = 2 | |
$$ | |
は同時に成り立たない。 | |
数学の世界にデフォルトで「時間の流れ」みたいなものは | |
表現されませんから、このコードは論理破綻しているわけです。 | |
論理破綻なんかしていない!とフツーに思ってしまうのは、 | |
無意識にコードの上のほうとコードの下のほうでは、 | |
時間が異なっていて、命令実行される「前」と「後」という | |
時間変化を暗黙の了解としてしまっているからです。 | |
`a = 1` | |
であるところに、無造作に | |
`a = 2` | |
としてしまうのは**論理を破壊**するので、 | |
プログラミングでは**破壊的代入**と呼ばれており、 | |
こういう操作は**関数型プログラミングではNG**です。 | |
##変数にはきちんとインデックスをふる | |
では、どのようにして論理的整合性を取るのか?というと、 | |
**変数にきちんとインデックスを振り分けてやって** | |
**別の独立した存在として扱う。** | |
この場合、時間変化するのだから、 | |
インデックスを`time`の頭文字をとって | |
$$ | |
t | |
$$ | |
としてやることにしましょう。 | |
任意の時間` t` における | |
$$ | |
aとb | |
$$ | |
は、 | |
$$ | |
a_tとb_t | |
$$ | |
と表現することにします。 | |
これで、**時間変化にともない自動的にtの値が異なる**ので、 | |
たとえ**破壊的代入をしようとおもっても絶対に無理**になりました。 | |
破壊的代入っていうのは、「後から」代入し直すという行為なので、 | |
「後」ってことは、tの時間が自動的に異なるからです。 | |
$$ | |
a_tとb_t | |
$$ | |
は、時間変化から完全に自由になり、 | |
完全に論理的整合性のある存在に変貌したのです。 | |
同じように、 | |
$$ | |
b_t = a_t \times 5 | |
$$ | |
というのも時間tにおけるaとbの関係性、ということで、 | |
意味がより明確になりました。 | |
実際これは、 | |
「時間変化`t`において、リアクティブである」 | |
というのを数学的に厳密に表現している式です。 | |
同様に、 | |
$$ | |
console.log(b_t);\\ | |
$$ | |
となります。 | |
これは、 | |
「時間変化`t`において、 | |
bのマス目はリアクティブに再計算されて再描画される」 | |
というのを数学的に厳密に表現している式です。 | |
##もしもコードを数学にする | |
材料は全部出揃ったわけで、 | |
最初のコード | |
```js | |
var a, b; //Excelのマス目に相当する | |
a = 1; //aのマス目の値は 1 | |
b = a * 5; //bのマス目の値は、aのマス目の値の5倍と等しい | |
a = 2; //aのマス目の値を 2に変更 | |
console.log(b); // bのマス目はリアクティブに再計算され | |
//10 が表示される!! | |
``` | |
を数学的に厳密に書き換えると、 | |
$$ | |
b_t = a_t \times 5\\ | |
$$ | |
$$ | |
console.log(b_t);\\ | |
$$ | |
$$ | |
a_0 = 1\\ | |
$$ | |
$$ | |
a_1 = 2\\ | |
$$ | |
となります。 | |
##数学をリアルJavaScriptコードにする | |
この数学を、実際のJavaScriptに再展開してやりたいわけです。 | |
そこで、 | |
[【後編】2015年、脱オブジェクト指向時代にFacebook-Reactのコードをイミュータブルな宣言型でイベント駆動するFRPで書く 論理の物質化をするな!](http://qiita.com/kenokabe/items/385a80295046fb9ada6d) | |
の後のほうでも出した、 | |
筆者のコンセプチュアルなFRPライブラリを適用します。 | |
SpaceTimeLine A conceptual FRP model library | |
GitHub | |
https://github.com/kenokabe/spacetimeline | |
npm | |
https://www.npmjs.com/package/spacetimeline | |
すると、こう書けます。 | |
$$ | |
a_t | |
$$ | |
は、`var a = _();` で表現します。 | |
$$ | |
b_t = a_t \times 5 | |
$$ | |
は、`var b = a.map(function(n){return n * 5;});` | |
で表現します。 | |
$$ | |
console.log(b_t); | |
$$ | |
は、`b.compute(console.log);` で表現します。 | |
**ここはExcelのマス目の自動再計算の再描画宣言に相当します。** | |
最後に、 | |
$$ | |
a_0 = 1\\ | |
$$ | |
$$ | |
a_1 = 2\\ | |
$$ | |
時間軸において、コードが解釈実行された時刻で`appear`する。 | |
以上、全部合わせると、 | |
```js | |
var _ = require('spacetimeline'); | |
var a = _(); | |
var b = a.map(function(n){return n * 5;}); | |
b.compute(console.log); | |
a.appear(1); | |
a.appear(2); | |
``` | |
コードの中核部分は、 | |
```js | |
var a = _(); | |
var b = a.map(function(n){return n * 5;}); | |
b.compute(console.log); | |
``` | |
この3行です。 | |
- `a`の宣言 | |
- `b`の宣言(`a`との論理関係を宣言) | |
- 最後のコンソールへ再描画する宣言。 | |
この3行だけです。 | |
##Facebook-Reactにする | |
$$ | |
a_tとb_t | |
$$ | |
は、**時間変化から完全に自由になり、** | |
**完全に論理的整合性のある存在**に変貌しました。 | |
**これがそのままFacebook-Reactのコンポーネントになります。** | |
$$ | |
a_t | |
$$ | |
は、`TodoList` | |
$$ | |
b_t | |
$$ | |
は、`TodoApp` | |
$$ | |
console.log(b_t); | |
$$ | |
は、 | |
筆者のさっきのFRPライブラリでは、 | |
`b.compute(console.log);` | |
でしたが、 | |
Reactではもちろん、 | |
`React.render(<TodoApp />, mountNode);` | |
になります。 | |
**ここはExcelのマス目の自動再計算の再描画宣言に相当します。** | |
コードの中核部分はこの3行です。 | |
- `TodoList`の宣言 | |
- `TodoApp`の宣言(`TodoList`との論理関係を宣言) | |
- 最後のDOMへ再描画する宣言。 | |
この3行だけです。 | |
Facebook-ReactのToDoリストアプリのコードは以下です。 | |
http://facebook.github.io/react/ | |
よーくみると、 | |
- `TodoList`の宣言 | |
- `TodoApp`の宣言(`TodoList`との論理関係を宣言) | |
- 最後のDOMへ再描画する宣言 | |
のたった3行だけで出来ており、 | |
さっきの筆者のFRPのコード | |
- `a`の宣言 | |
- `b`の宣言(`a`との論理関係を宣言) | |
- 最後のコンソールへ再描画する宣言。 | |
とまったく同じ構造になっていますね? | |
```js | |
var TodoList = React.createClass({ | |
render: function() { | |
var createItem = function(itemText) { | |
return <li>{itemText}</li>; | |
}; | |
return <ul>{this.props.items.map(createItem)}</ul>; | |
} | |
}); | |
var TodoApp = React.createClass({ | |
getInitialState: function() { | |
return {items: [], text: ''}; | |
}, | |
onChange: function(e) { | |
this.setState({text: e.target.value}); | |
}, | |
handleSubmit: function(e) { | |
e.preventDefault(); | |
var nextItems = this.state.items.concat([this.state.text]); | |
var nextText = ''; | |
this.setState({items: nextItems, text: nextText}); | |
}, | |
render: function() { | |
return ( | |
<div> | |
<h3>TODO</h3> | |
<TodoList items={this.state.items} /> | |
<form onSubmit={this.handleSubmit}> | |
<input onChange={this.onChange} value={this.state.text} /> | |
<button>{'Add #' + (this.state.items.length + 1)}</button> | |
</form> | |
</div> | |
); | |
} | |
}); | |
React.render(<TodoApp />, mountNode); | |
``` | |
おわり。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment