あけましておめでとうございます!!今年は友人のサークルで売り子をしていたのですが、なんと完売御礼でした!!やったねたえちゃん!(謎
さて元日も過ぎて餅にも飽きてきた頃かと思いますので、半年間掛けて未踏IT人材育成事業で開発していたもののうち、なんとか外に出せそうなスクリプト言語部分について紹介します!
ど〜なっつ、時を司るスクリプト言語
ど~なっつは
- 処理系とその外界の状態を過去に戻し、未来に戻せ、
- 任意の時点でのセーブデータを書き出し、後で実行を再開できる、
ちょっと変わった「時を司るプログラミング言語」です!
時を司るプログラミング言語
普通、プログラミング言語って上から下に実行されて、起こった事は元に戻りません。副作用ってやつです。覆水盆に返らず。
int main(){
int x = 10; //このときxは10。
printf("x is %d", x);
x = 20; //これ以降、ずっと20で、以前10だった痕跡は一切残らない。
printf("x is %d", x);
return 0;
}
タイムリープでフロー制御。
でも、戻りたい時だってあるかもしれません。例えば、ユーザーのログイン処理なんかそうでしょうか。普通のプログラミング言語、例えばRubyだと、こんな感じでパスワード認証を書くでしょうか?
while true do
puts "パスワードを入力して下さい!"
passwd = gets
if passwd == "正しいパスワード" then
puts "認証完了"
break
end
puts "間違ったパスワードです"
end
puts "こんにちは、正しいユーザーさん!"
すっきり書けました。
でも…。やりたい事は
ではなくて、
- 正しいパスワードが入力されたらログインしていいけど、そうじゃなかったら門前払い
なのではないでしょうか?「ど~なっつ」では、こんな感じで同じプログラムを書けます!
//このあとの命令を使って、この時点まで時を戻せるようになります。
save_time=Homura.tick();
System.print("パスワードを入力してください。");
//パスワードを読み込みます。
passwd=System.readline();
if(passwd == "abcdef"){
//パスワードの認証に成功しました。
System.println("こんにちは!正しいユーザーさん。");
} else {
// 上で記録した時間まで、スクリプトエンジン全体を戻します。
System.println("パスワードが間違っています!");
Homura.seek(save_time);
};
「Homura」というオブジェクトを使って、パスワードが間違っていたときはログイン処理前にまでタイムリープさせてしまいます。変数、コールスタック、全ての「環境」が文字通り「戻ります」。
これで不自然な繰り返しを使わずにユーザーログイン処理が書けてしまうのです!(ドヤァ
私は何度でも繰り返す。
gotoでも同じように書けますが、変数も全て巻き戻されるので、安全に巻き戻す事ができます。この事を利用すると、「時が戻る事を利用したループ」がつくれます。普通に起こすと無限ループになりそうですが、ほむらオブジェクトに突っ込んだ値はタイムリープ前の事を覚えているので、コレを使います。交わした約束忘れないよ
//普通の変数は、時間操作の影響を受けますが…
tabeta=0;
//Homuraは時間操作前の事を覚えていて、時間操作の影響を受けません。
Homura.counter=0;
//このあとの命令を使って、この時点まで時を戻せるようになります。
save_time=Homura.tick();
if(Homura.counter < 10){
// ドーナッツを食べましょう。
// この足し算の結果は、時間操作で戻ってしまいます。
tabeta++;
// ドーナッツを食べた回数を表示しましょう
System.println(tabeta, "番目のドーナッツを食べた!");
// Homuraに入った値は時間操作の影響を受けないので、
// 時間操作を行なってもこの足し算の結果は戻りません。
Homura.counter+=1;
// 上で記録した時間まで、スクリプトエンジン全体を戻します。
Homura.seek(save_time);
} else {
//ど~なっつでは、else節を省略することはできません。
};
これを実行すると、
% donut time_op.donut
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
1番目のドーナッツを食べた!
このように何度ループをめぐっても1番めのドーナッツを食べ続けることができますが、Homuraは時間操作の影響を受けないので、無限ループには陥らずに済みます。
「継続」と違って スクリプト外部への副作用も制御できる
こういったループはSchemeのcall/ccやRubyのFiberような「継続」を使えば記述することができます。しかし、「継続」のアプローチでは戻せないものがあります。それは、言語処理系外の環境です。
つまり、Scheme内から外界(たとえばウィンドウシステム)に、「ウインドウを表示する」と言った副作用を起こしていた場合、「ウインドウを表示する」以前の継続に戻っても、戻るのはSchemeの中でだけであって、ウインドウは表示されたままになってしまいます。
「ど〜なっつ」では言語処理系外に起こした副作用も、「副作用」に対応した「反副作用」みたいなものを登録して管理させることができます。時間操作が行われた瞬間にその「反副作用」を用いて副作用を打ち消させることができます。
でも、「反副作用」を作れない場合もありますよね?例えば、ネットワークでデータを送信するような操作は絶対取り消せませんから、「反副作用」は作れません。そのときにその時刻以前には戻れないようにする機構も作られています。コンソールへの出力も取り消せませんから、今回使ってるSystem.printlnなども本当は取り消せないメソッドとして登録すべきなのですが、副作用のないメソッドとして登録されています。HaskellのUnsafeIOみたいな感じです。
普通にスクリプト言語
関数も使えますし、関数はクロージャーですし、第一級オブジェクトです。JavaScriptのような感じで使えます。
f = func(idx, acc){
if( idx <= 0 ){
acc; //最後に評価された値が戻り値になる
}else{
return f(idx-1, acc+idx); //戻り値を明示することもできます。
};
};
System.println(f(10, 0)); // prints 55
またプロトタイプ型オブジェクト指向言語で、__proto__にオブジェクトを指定するとJSのようにプロトタイプを巡ってくれます(この辺のドキュメントも書かなきゃ…)。全体的にはJavaScript: The Good Parts ―「良いパーツ」によるベストプラクティスを参考に設計されています。
まだまだ開発中で、リポジトリも未踏でのプロジェクトと同じリポジトリに入っていて大変使いづらいとは思うのですが、テンションを上げるために公開してみました。
ダウンロード
ダウンロードはど~なっつのサイトからお願いします!githubでのソースは本体プロジェクトの「ちさ」と同じ所に入ってます。
ライセンス
ど~なっつのライセンスはGPLv3となっています。
他に開発中のもの
他に開発中の本体プロジェクト「ちさ」は、このど~なっつをネイティブで使用して、時を司り、さらに任意の時点での実行状態を保存できるGUIツールキット兼電子コンテンツエンジンです。が、まだあんまり出来てないので公開はまだしません。
Thanks
ど~なっつはIPAの未踏IT人材発掘・育成事業に採択された「CPUの理解を容易にするシステムと解説サイトの構築」の一部分として開発され、支援を受けています。