Every repository with this icon (
Every repository with this icon (
Shibuya.js in Kyoto
Arrow.js
Shibuya.js in Kyoto
motemen (motemen@gmail.com)
Arrow
Arrow というのが Haskell 界隈で流行ってるらしい。
関数 (など) を矢印 (arrow) と見做して、矢印を連結することで処理を繋げていく
Arrow 同士を連結したものもまた arrow になる。
Arrow を作る/実行する
値を一つとって一つ返す関数から arrow を作る: Arrow(f)
Arrow.prototype.run(value) は Arrow に値を一つ渡して実行するメソッド
this.cpsFunction
CPS: Continuation Pasing Style
普通の (同期な) コード
function(x) { // … return(x); }CPS なコード
function(x, k) { // … k(x); }同期なコードも非同期なコードもコールバック形式にすることで同様に扱える。
Arrow(function(x) { return x + 1 }).run(1); // => 2Arrow の連結 (>>>)
Haskell の演算子をそのままメソッド名にしているため a['>>>'](b) とする必要がある。@(a)[‘>>>’](b)@ とすることでより納得のいく形になります。:)
「値を a に渡し、その結果を b に渡す」arrow を作る演算子。
var aLoadIndex = Arrow.XHR('index.html');
var aAlert = Arrow(alert);
var aGetAlert = (aGetIndex)['>>>'](aAlert);
aGetAlert.run(); // index.html を xhr でロードして結果を alert する
複数値 (***, &&&)
値を複数の arrow で受け取る
***: 配列を分配
((a)['***'](b)['***'](c)).run([1, 2, 3])
// a(1), b(2), c(3) が実行される
&&&: 一つの入力をすべての arrow に渡す
((aLoadJSON1)['&&&'](aLoadJSON2))['>>>'](aAlert).run()
// aLoadJSON1(undefined), aLoadJSON2(undefined)
// => [json1, json2]
非同期な arrow は全部の結果が返ってくるまで待つ
条件分岐 (|||, +++)
入力値によって複数の arrow のうち 1つを選択する
Arrow は値を 1つしか受け取れないため、値をラップするものが必要:
Arrow.Value.In(n)(x) → n 番目の arrow に値 x を渡す
+++: 入力値で条件分岐
((aHoge)['+++'](aFuga)['+++'](aPiyo))
.run(Arrow.Value.In(0)(x)) // => aHoge.run(x);
|||: 条件分岐して、結果をまたラップしなおす
「a もしくは b, そのあと f もしくは g」という感じ
(((a)['|||'](b))['>>>']((f)['|||'](g)))
.run(Arrow.Value.In(1)(x)) // => g(b(x))
Arrow.Error
Arrow 内で例外が発生した場合はエラーオブジェクトが返ってくる
Arrow.Error = Arrow.Value.In(1);マージ (<+>)
複数の arrow から、最初に返ってきた arrow の値を採用
全部返ってくるまで待つ &&& とは違って…
((aLoadJSON1)['<+>'](aLoadJSON2)['<+>'](aLoadJSON3))['>>>'](aAlert).run()
// => json1 か json2 か json3, 一番早い 1つだけ
Arrow の繰り返し
Arrow.Loop(arrow)または
arrow.loop()arrow の返した値をまた arrow に渡してまた… という arrow
→ イベント処理などで使える (あとで実例を)
非同期な Arrow の例
Arrow.Event(node, event)
DOM イベントが発生したら次の arrow に進む
h3 Arrow.Delay(msec)
msec ミリ秒後に次の arrow に進む
Arrow.Stop
何があっても次に続かない
→
(Arrow.Stop)['<+>'](aHoge) は aHoge と同じ
実例
GitHub プレゼンテーションぐりもん
仕様
- (GitHub の wiki ページ上で) alt-p キーでプレゼンモードに入る
- プレゼンモードでは j, k でページを進む/戻る
- もちろんそれ以外のキーは無視
プレゼンモードの状態遷移図は…

プレゼンモードの arrow は
var aSlide =
(Arrow.Event(document, 'keydown'))
['>>>']
(
(aIsKey('j')['>>>'](aNextPage))
['<+>']
(aIsKey('k')['>>>'](aPrevPage))
['<+>']
(aNOP)
)
['>>>']
(aShowCurrentPage);
状態遷移図をそのままコードに落としただけ!(状態遷移図における矢印が arrow に対応している)
Arrow.Event(document, 'keydown') により、ドキュメントで keydown イベントが発生すると次の arrow にイベントオブジェクトが渡される
aIsKey(key) というのは、キーイベントオブジェクトのキーが key である時だけ次の arrow に処理を渡す arrow (というのを作っておく), これと <+> を組み合わせると:
- “j” キーが押されたとき: aIsKey(‘j’)[‘>>>’](aNextPage) が実行される
- “k” キーが押されたとき: 上の arrow が次に進まないので、aIsKey(‘k’)[‘>>>’](aPrevPage) が実行される
- その他のキーが押されたとき: 上の arrow たちが次に進まないので、aNOP (何もしない arrow) に処理が移る
これらのうちどれかが実行されたあと、現在のページを表示する aShowCurrentPage が実行される
プレゼンモードに入る前の状態遷移図は…

var aStart =
(Arrow.Event(document, 'keydown'))
['>>>']
(
(aIsKey('alt-p')['>>>'](Arrow.Value.In(0)))
['<+>']
(Arrow.Value.In(1))
)
['>>>']
(
((aSetupSlide)['>>>'](aSlide.loop()))
['+++']
(aNOP)
);
最初の arrow:
alt-pが押されたら 0番目のルートを選ぶ- そうでなければ 1番目のルートを選ぶ
その結果を受け取る arrow:
- 0番目のルートでは: さっきの
aSlideを繰り返す arrow (もう返ってこない) - 1番目のルートでは: 何もしない
これらの arrow を宣言した上で、
aStart.loop().run()とするとコードが動き始める…!
loop() しているため aNOP の後は最初に戻ってまた keydown をリスンする
いちおう
英語のメソッド名もあるけど見易いかどうかは…
var aSlide =
(Arrow.Event(document, 'keydown'))
.next(
(aIsKey('j').next(aNextPage))
.or
(aIsKey('k').next(aPrevPage))
.or
(aNOP)
)
.next(aShowCurrentPage);
Arrow 便利な気がしてきた
自分でも半信半疑だったけどわりと便利な気がしてきた!






