FRPの川つくった

GUI作るの辛い。オブジェクト指向コンポーネントは限界がある。時代はやっぱFRPか?と思ってRx.jsとbacon.jsとか触ってみたんだけど。難しすぎて発狂した。

オブジェクト指向はいらない子? - Togetterまとめ

FRP

エクセルのセルがなんか他のセルの状態によって変化するようなやつ(雑)

bacon.jsが難しすぎる

baconjs/bacon.js

こういうのがたくさんある。

  • 合流のさせ方がたくさんあって難しい
  • イベントストリームが全状態を保存しているみたいで、メモリが不安
  • なぜかjQuery用のDSLがたくさんあって覚えるの面倒だし別にそういうのは望んでない

本当にやりたいことは?

  • ストリーム作って
  • 合流させて
  • その値が変化したタイミングを知りたい
  • (ついでにメモリ圧迫したくない)
  • (あと依存なしでnodeでもブラウザでも)

川の流れをつくった

mizchi/kawa.js

new Kawa.Stream(初期値, reduce関数) でストリームを作る

sumStream = new Kawa.Stream 0, (v, sum) -> v + sum # 初期値と集約関数
sumStream.onChange (v) => console.log v

# addSourceでストリームに値を追加する
sumStream.addSource 1 # 1
sumStream.addSource 2 # 3

reduce関数を省略したら最後にaddSourceされた値を返す関数(val)-> valをデフォルトで使う。 Kawa.merge でストリームを合流させた新しい川のストリームを作れる。

s1 = new Kawa.Stream 0
s2 = new Kawa.Stream 0
merged = Kawa.merge 0, [s1, s2], ([v1, v2], last) -> v1 + v2
merged.onChange (v) -> console.log 'merged:', v

s1.addSource 2 # -> merged: 2
s2.addSource 2 # -> merged: 4

s1, s2どっちの値が変わっても、merged.onChangeは発火する。(同値チェックはしてない。プリミティブ値以外が見れなくなるため)

川が特定の状態になったらコールバックを発火したい。

Kawa.when [s1, s2], (([v1, v2]) -> v1 is v2 * 2), -> console.log 'fullfilled!'
s1.addSource 2
s2.addSource 1

s1がs2の二倍になったら発火する。whenじゃなくてonceだったら一回しか実行されない。

すべての川のStreamはdisposeで破棄可能。メモリを漏らさない。

s1 = new Kawa.Stream 0
s2 = new Kawa.Stream 0
merged = Kawa.merge 0, [s1, s2], ([v1, v2], last) -> v1 + v2
waiter = Kawa.when [s1, s2], (([v1, v2]) -> v1 is v2 * 2), -> console.log 'fullfilled!'

# ...

merged.dispose()
s1.dispose()
s2.dispose()
waiter.dispose()

まとめ

  • bacon.jsは難しすぎる
  • 自分で単機能な実装したら150行だった
  • エクセルとか実装したいときに便利だと思う
  • 普通の人間はエクセルを実装しない