この前作ったkakahiaka.jsを、たまに簡単なWebViewのアプリとかで使ってみてた。小回りが利いていい感じ。
コンセプトはPastaと全く一緒で、関数ベースのMVCをやるとしたらどうなるかっていうのがテーマなんだけど、Pastaは今となってはAPIがださいのでもうちょい関数型言語、というかLispのライブラリっぽいAPIにして作り直したのがkakahiaka。ちなみにkakahiakaはハワイ語で朝って意味です。
OOみたいに状態がそこら中に散らばって今何が起きているのかわからないっていうのはもちろん問題なんだけど、FRPみたいに状態をストリームの中に隠しちゃうのも僕には扱いづらかった。 Pastaとkakahiakaはアプリの状態をファーストクラスにしてしまうことで、アプリの状態の把握とか永続化とか復元とかロールバックを簡単にできるようにしている。
サンプルコード:
var K = kakahiaka; //アプリの最新の状態を持つatomを作る var app = K.app({ todos: [] }, persist, recover); //呼ばれたらappの状態にパッチを当てる関数を定義する var add_todo = K.deftransition(function (state, title) { //todoを追加 -- 状態に対する差分を返す return { todos: _.conj(state.todos, { title: title , completed_p: false }) }; }); //アプリの状態に変更があった時に副作用を起こしてGUIを更新する K.watch_transition(app, "todos", function (state) { $("#todos").html(JSON.stringify(state.todos)); }); //ビューでイベントが起きたらトランジション関数を呼ぶ $(function () { $("#todo-submit").on("click", function () { var title = $("#todo-title").val(); add_todo(app, title); }); }); //ヘルパ -- アプリの状態の永続化と復元 function persist (state) { localStorage.setItem("sample_app_state", JSON.stringify(state)); } function recover () { return JSON.parse(localStorage.getItem("sample_app_state") || "{}"); }
deftransition
で作る関数群がModel、
watch_transition
で登録するリスナ群がView、
deftransition
で作った関数を呼ぶコードがController
にそれぞれ緩やかに対応する。
APIコールとかは普通にトップレベルで関数を定義してその中でレスポンスが来たらトランジション関数を呼ぶようにしておいて、ボタンが押された時とかにこれを呼べばいい。
DOMを操作するコードを全てwatch_transition
の中に書けば、自動的に 同じ状態を与えられたら同じGUIを返す 言わば参照透明なViewができる。これがアプリの状態の復元とかロールバックとかを可能にする。