nodeのFRP(関数型リアクティブ)ライブラリworldcomponent
の後継、worldtimestream
をnpmで公開をしました。
https://www.npmjs.com/package/worldtimestream
https://github.com/kenokabe/worldtimestream
FacebookのJSXで開発テストしており、ライブラリもテストも最終的にバニラJavaScriptへコンパイルしています。
Install
node / io.js
npm install worldtimestream
WebBrowser
https://raw.githubusercontent.com/kenokabe/worldtimestream/master/js/worldtimestream.js
をコピーペースト
Rationale
アイデアは、前回の記事です。
Date.now() はストリームへ透過に参照する時間関数ですが「ユーザの現在時間」が暗黙に引数として渡されているため理解しにくいです。
より物理系と時間という物理量の概念を関数型のコードで明示できるように、FRPライブラリをハックしました。
worldtimestream
は命名したとおり、時間軸上のストリームです。
時間要素に依存しているので、worldtimestream
のストリームは物理量です。
___.world = ___((t) => {
//...........
});
という時間発展の恒等式を宣言することにより、
古典物理学の物理量である時間変数t
が得られます。
別に時間変数now
と宣言しても構いませんが、現在時間というのは、なかなか哲学的素養がないと理解しにくい概念であるので、恒等的な値t
として宣言します。
以下、すべて基礎となる上記のスコープ内で記述します。
上記コードにより、
時間発展しているworldtimestream
のストリームの物理系の物理量t
が、↓
時間発展しているユーザが現在実行しているコード
へ恒等的にmap
されているので、
t()
とすることにより、それぞれの瞬間のユーザは透過にその値を参照できます。
つまりそれが、時間発展している世界のそれぞれの瞬間のユーザにとっての「現在時刻」であるということになります。
たとえば、
___.world = ___.log(t());
という恒等式を宣言しておくと、
このコードを意識的に計算するアクションを起こした、すべてのユーザのそれぞれの現在時刻がコンソールへ表示されます。
また、ストリーム___a
を宣言しておき、
var ___a = ___();
___.world = ___a.compute(() => {
___.world = ___.log(t(), 'a', ___a.t());
});
と恒等式を宣言しておくと、
時間発展するストリーム___a
に
「過去」から「未来」方向
あるいは
「未来」から「過去」方向
で変化が生じた瞬間に、ユーザの関与なしに任意のコードを自動計算させることが可能です。
var f0 = () => {
___.world = ___.log('test start');
___.world = ___a.appear(0);
};
___.world = t.computeTimeout(f0, 0);
というのは、このコード全体が任意のユーザによって最初に実行された瞬間t
における、ストリーム___a
の値を宣言しています。
var f = () => {
___.world = ___a.appear(___a.t() + 1);
};
___.world = t.computeInterval(f, 1000);
とすることで、そこ瞬間を起点として、ストリームの時間軸上で「未来」方向に1秒間のインターバルの値を宣言しています。
この場合は、ストリーム___a
のそのインターバル地点のある時間t
における値が参照されており、その値に1を加えた値をストリーム___a
の未来方向の次の瞬間に現れているという「関係性」を宣言しています。
念の為ですが、ストリーム___a
は過去から未来まで時間軸上でイミュータブルで変化しません。ストリーム上に離散する値の関係性が上記コードによって宣言されているだけです。
たとえばこのストリーム___a
をオブジェクト参照などで、コピーあるいは「バックアップ」すれば、過去から未来までまったく同じストリームがコピーされます。
つまり、未来方向の任意の時間で、
___a
の値がappear
されるという宣言があるのならば、まったく同じイミュータブルなストリームである___b
もその同時の瞬間に値に「変化」が起こるストリームとして宣言されます。
過去未来の時間軸上で別の運命をもつ別のストリームを作成するには、同様に
var ___b = ___();
というような別個の独立したストリームを宣言します。
もし、この独立したストリーム___b
が、
ストリーム___a
と関係がある場合は、その都度その関係式を宣言します。
たとえば、
___a
のストリーム上のある瞬間の値が、
___b
のストリーム上の同一瞬間の値と等しくなり、「未来方向」へはずっとその値のままである、という我々が日常レベルで使う用語としての「値のバックアップ」という関係を宣言したいのであれば、
___.world = ___b.appear(a.t());
とすれば簡単に出来ます。
あるいは、
___a
と___b
は、物理量t
に依存しない形で、恒等的な関係が存在するとき、
たとえば
___b = ___a x 5
の関係を宣言したい場合は、
___.world = ___a.compute((x) => {
___.world = ___b.appear(x * 5);
});
とすれば簡単に宣言できます。
Test
test.jsx
'use strict';
var ___ = require('./worldtimestream.js');
___.world = ___((t) => {
var ___a = ___();
var ___b = ___();
___.world = ___a.compute(() => {
___.world = ___.log(t(), 'a', ___a.t());
});
___.world = ___b.compute(() => {
___.world = ___.log(t(), 'b', ___b.t());
});
___.world = ___a.compute((x) => {
___.world = ___b.appear(x * 5);
});
var f0 = () => {
___.world = ___.log('test start');
___.world = ___a.appear(0);
};
var f = () => {
___.world = ___a.appear(___a.t() + 1);
};
___.world = t.computeTimeout(f0, 0);
___.world = t.computeInterval(f, 1000);
});
実際に、test.jsxを実行した結果は以下のようになります。
1438234695190 ‘a’ 0
1438234695200 ‘b’ 0
1438234696204 ‘a’ 1
1438234696204 ‘b’ 5
1438234697206 ‘a’ 2
1438234697206 ‘b’ 10
1438234698208 ‘a’ 3
1438234698208 ‘b’ 15
1438234699209 ‘a’ 4
1438234699209 ‘b’ 20
1438234700211 ‘a’ 5
1438234700211 ‘b’ 25
1438234701213 ‘a’ 6
1438234701213 ‘b’ 30
1438234702214 ‘a’ 7
1438234702214 ‘b’ 35
1438234703215 ‘a’ 8
1438234703215 ‘b’ 40
0 コメント:
コメントを投稿