Today アクセスカウンター Yesterday アクセスカウンター

ホワット・ア・ワンダフル・ワールド

私は知識に何ものかを付け加え,また他の人々がより多くのものを付け加える手助けをした --- G.H.ハーディ

全記事一覧 << 2008/07 12345678910111213141516171819202122232425262728293031 2008/09 >>

プロフィール

あろは (alohakun)

  • Author:あろは (alohakun)
  • 若槻俊宏 (WAKATSUKI toshihiro)

    連絡先 : alohakun ___at___ gmail.com
    mixi : http://mixi.jp/show_friend.pl?id=182927
    twitter : http://twitter.com/alohakun

    abstract

    プログラミングという人間の知的行為を体系化し,単なる職人芸ではなく,サイエンスにするための研究をしています.

    具体的には,等価変換計算モデルに基づいた,仕様記述からのプログラム合成の研究をしています.

    もっと噛み砕くと,プログラムの正しさをどのように定式化し,どのような枠組みで,どのように変換を進めていけば,正しさを保証したまま,効率的なプログラムを手に入れることができるのか,ということについて研究しています.

    キーワード : equivalent transformation, computation model, programming paradigm, formal specification, program synthesis













    あわせて読みたい


    この日記のはてなブックマーク数


    スカウター : ホワット・ア・ワンダフル・ワールド


    Map









    FC2 BLOG RANKING

FC2カウンター

ブロとも申請フォーム

この人とブロともになる

limbo のパラメトリック多相

2007/04/17(火) 09:27:04

実験中の実装で,あんまり真面目に開発もされてない感じですが,limbo にパラメトリック多相を導入しようという話があるようです (2003 年からあんまり動いてない感じ).

パラメトリック多相ってのは,C++ のテンプレートみたいなものですが,型の数だけ実装 (オブジェクトコード) が生成されたりはしません.要するに,コンパイラによって型チェックされる void* みたいなものです.与えられた引数の型から,型変数の値 (型) をコンパイラが型推論 (type-reconstruction) して整合性をチェックします.

こんな感じ.ref 型 (reference type) のリストならば何でも reverse することができます.


implement reverseT;

include "sys.m";
sys: Sys;
include "draw.m";

reverseT: module {
init: fn(nil: ref Draw->Context, nil: list of string);
reverse: fn[T](l: list of T): list of T;
};

reverse[T](l: list of T): list of T {
r: list of T;
for(; l != nil; l = tl l)
r = hd l :: r;
return r;
}

Integer: adt {
i: int;
print: fn(s: self Integer);
};

Integer.print(s: self Integer) {
sys->print(" %d ", s.i);
}

init(nil: ref Draw->Context, nil: list of string) {
sys = load Sys Sys->PATH;

stringList := reverse("one" :: "two" :: "three" :: nil);
for(si := stringList; si != nil; si = tl si)
sys->print(" %s ", hd si);

intList := reverse(ref Integer(1) :: ref Integer(2) :: ref Integer(3) :: nil);
for(ii := intList; ii != nil; ii = tl ii)
(*(hd ii)).print();
}


% limbo reverseT.b
% ./reverseT
three two one 3 2 1

ここらへんまでくると,だんだんと型推論がある言語っぽくなってきます.

まぁ,わざわざ実装しなくても,list.m というモジュールの中に reverse はあるんですが.list モジュールのような limbo のコンテナライブラリの多くはパラメトリック多相で実装してあります.

include "lists.m"
lists := load Lists Lists->PATH;

append: fn[T](l: list of T, x: T): list of T;
combine: fn[T](l1: list of T, l2: list of T): list of T;
concat: fn[T](l1: list of T, l2: list of T): list of T;
delete: fn[T](x: T, l: list of T): list of T for { T => eq: fn(a, b: T): int; };
ismember: fn[T](x: T, l: list of T): int for { T => eq: fn(a, b: T): int; };
last: fn[T](l: list of T): T;
pair: fn[T1, T2](l1: list of T1, l2: list of T2): list of (T1, T2);
reverse: fn[T](l: list of T): list of T;
unpair: fn[T1, T2](l: list of (T1, T2)): (list of T1, list of T2);
allsat: fn[T](p: ref fn(x: T): int, l: list of T): int;
anysat: fn[T](p: ref fn(x: T): int, l: list of T): int;
filter: fn[T](p: ref fn(x: T): int, l: list of T): list of T;
map: fn[T](f: ref fn(x: T): T, l: list of T): list of T;
partition: fn[T](p: ref fn(x: T): int, l: list of T): (list of T, list of T);

limbo の parametric polymorphism は,The limbo programming language にも載ってない (1999 あたりで更新が停止してる) ので,なかなか謎が謎を呼ぶ構文です.for とかは,T に対して,T が持っていなければならないメソッドに関する制約を記述しているような予感がします.いわゆる,継承関係とかを無視して,おおらかなガチョウのお母さんの気持ちになり,とりあえずガーと鳴く子ならウチの子という,ダック・タイピングというやつですね (本当か ?) R なんとかとか P ほげみたいなルーズな言語とは違って,鳴けない子を渡すとちゃんとコンパイルタイムにエラーになるはずです.

もう 20 時間ほど起きていて,わりとフラフラなので英語読めません.後で読む.

[9fans] limbo polymorphism
[9fans] Re: advantages of limbo
Plan9/InfernoTB:0CM:0 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!

Inferno と MapReduce と functional grid programming

2007/04/15(日) 16:51:55


Without understanding functional programming, you can't invent MapReduce, the algorithm that makes Google so massively scalable. --- Joel Spolsky

んなこたーない.Java や C# が多少関数プログラミングをサポートしたところで,MapReduce と functional programming は直交している.

ようするに,Java は C 言語の仲間,単一 CPU とフラットなメモリ空間をモデルに持つ低水準な手続き型言語というのが問題なのであって,無駄に論点をすり返るのは賢明では無いと思う.

ところで,最近 Erlang が静かに流行っているようだ (静かなブームってってよくいうけど,それって結局流行ってないってことなんだよね by なにわ小吉).

# jmuk
いつのまにか Erlang スレが立っていることに気付いた
ruiさんのしわざ?
そしていきなりスゲー荒らされている orz

# kimura
本当だ。これはひどい。

# rui
オレじゃないwww

# rui
Common Lispスレでも話題になってたから誰かが立ててもおかしくない > erlangスレ

# jmuk
なんか Haskell スレを「本スレ」と呼んでいる書き込みもあったので、似たようなメンツで構成されているらしい

# rui
HaskellとErlangはぜんぜん違うやん

# jmuk
そうですね。なんで本スレなのかは私には理解できません
# いちおう「純関数型」という共通項があるくらい……?

# rui
Erlangには副作用があるし、lazyでもないけどね・・・
そういえばProgramming Erlangの新しいPDFがダウンロード可能になったというメールがきたので続きを読んでみたら、map reduceで分散処理をしてみるという章が足されていた

# jmuk
おおー
本が出るまで待とうと思ってたんですが、読もうかなあ

# rui
Erlangでは簡単に分散処理ができるので、mapreduceといってもプロセスをspawnして結果をreduceするだけ
たいしたことない
Gauche@Lingr 2007/04/14

まぁ,日本の場合,中途半端に流行っても雑音が増えるだけなんだよなー.流行ろうがマイナーだろうが,有益な情報を発信できる人間の絶対数は変わらない気がする.

いや,Inferno はちょっとマイナー過ぎるので,もうすこし Plan9 方面の人々が流れ込んできて欲しいと思うけど.

そんなこんなで,limbo も,MapReduce に対して有利な言語なのではないかと思う.そもそも分散 OS は本質的に有利,というか,そのための分散 OS だし.Google が Rob Pike 氏を初めとした Plan9 の開発者たちを次々とヘッドハントしているというのも,ある意味当然至極.

と,ここまで書いた段階で,時間がきてしまったー.今日は近所でロマン・ポルシェのライブがあるので,この辺で.

後で読む.

inferno programmer's notebook lab 14 - map reduce functional grid programming
lab 37 - Geryon's mapreduce

たぶん,大量にプロセスを spawn して,階層的にチャネルに流し込んで行くだけで,十分なリソースがあれば filter & aggregator を実装できるような気がする.特殊なアルゴリズムを意識する必要すらない.limbo において,そもそも MapReduce などトピックですらない

まぁ,Inferno がどこまでスケールする実装なのかはよくわからないけど.時代はグリッドコンピューティングですよ !




追記 : まさしくこれはその通りだと思います.

lethevert 『そう。だから多分、あのMapReduceは分散OSの一種だと考えた方が、実際のものに近いのではないかというのが、元の記事の趣旨なんですよ。
そのあとあちらこちらでMapReduceライブラリ(!)を作ったというのは、本質を忘れて表層に走っていると。』 (2007/04/17 21:59)

MapReduce の場合はGFSを前提にして作っているので、XXXを使ってMapReduceライブラリを作ったYO、とか言っても、おいおいGFSまで実装したのかよみたいな、感じなので。そもそも、mapしてreduceするだけなら、シェルでもできそうなので、なんでもできるでしょ、みたいな気がする所を、まーネタだから、とわかっているうちはいいのでしょうけど、それを忘れてくると言葉だけが独り歩きしはじめて、一気にBuzzword化してしまって、もともとのものが持っていた問題意識とその解決策を忘れてしまって、またそのうち誰かが別の言葉で再発明して、みたいなループは生産的でないですからね。
Plan9/InfernoTB:0CM:0 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!

inferno にまともなシェルが欲しい

2007/04/13(金) 08:16:07

とりあえず,Inferno の端末の shell (wm/sh) に clear 機能を付けようといろいろがんばっていたのですが,ソースを読んでいたら,なんというか,これは shell とか inferno system の問題というよりも,wm/sh の作り方の問題のような気がしてきた.

要するに wm/sh は,Inferno tk で作られていて,描画周りはハードコードされているから,wm/sh の外側 (limbo で書かれたプログラムの実行) からは制御できない感じが.

というわけで,補完とか履歴とか端末のクリアとかを実現しようと思ったら,shell のインタフェースから作らないと駄目っぽい.

まぁ,そもそも /appl/wm/sh.b 自体が 800 行ちょいとコンパクトだし,作るのはインタフェースだけで,入力された文字列のパーシングとかインタプリートとかは sh モジュールに丸投げすれば何とか成りそうな気がする.

とりあえず,いつものようにガワだけ作ってみた.窓とテキストボックスとスクロールバーだけ.tk とか全くわからんから,必要そうな設定部分はほとんどコピペ (フォントを大きくしたり,微妙に小細工はしてある).tk/tkclient 一般のサンプルコードにもなっていると思う.


implement shellwindow;

include "sys.m";
include "draw.m";
include "tk.m";
include "tkclient.m";

shellwindow: module {
init: fn(ctxt: ref Draw->Context, argv: list of string);
};

init(ctxt: ref Draw->Context, argv: list of string) {
tk := load Tk Tk->PATH;
tkclient := load Tkclient Tkclient->PATH;

tkclient->init();
(top, ctl) := tkclient->toplevel(ctxt, nil, "shell", Tkclient->Appl);

tkclient->startinput(top, "ptr"::"kbd"::nil);
tkclient->onscreen(top, nil);

# shell window configuration commands from /appl/wm/sh.b
shwin_cfg := array[] of {
"frame .b -bd 1 -relief ridge",
"frame .ft -bd 0",
"scrollbar .ft.scroll -command {send scroll t}",
"text .ft.t -bd 1 -relief flat -yscrollcommand {send scroll s}"
+ "-bg white -selectforeground black -selectbackground #CCCCCC",
".ft.t tag configure sel -relief flat",
"pack .ft.scroll -side left -fill y",
"pack .ft.t -fill both -expand 1",
"pack .Wm_t -fill x",
"pack .b -anchor w -fill x",
"pack .ft -fill both -expand 1",
"focus .ft.t",
".ft.t configure -font /fonts/lucm/unicode.9.font",
"update"
};
for (i := 0; i < len shwin_cfg; i++)
tk->cmd(top, shwin_cfg[i]);

scroll := chan of string;
tk->namechan(top, scroll, "scroll");

for(;;) {
alt {
s := <-ctl or
s = <- top.ctxt.ctl or
s = <- top.wreq
=> tkclient->wmctl(top, s);
p := <- top.ctxt.ptr
=> tk->pointer(top, *p);
c := <- top.ctxt.kbd
=> tk->keyboard(top, c);
m := <- scroll
=>
if(m[0] == 't')
tk->cmd(top, ".ft.t yview " + m[1:] + ";update");
else
tk->cmd(top, ".ft.scroll set " + m[1:] + ";update");
}
}
}

limboshellprot20070413.png

limbo っていうか,ほとんど Inferno tk のプログラムだな… 文字列でコマンドを書くのが気持ち悪すぎる.

しっかし,tk って,どのくらいの機能があるのかさっぱりわからんのが不安.リファレンスとかもわかりにくい.あんまり開発されてなさげな方面に足を突っ込むと,すぐに,tk か Inferno の機能不足で無理 ! という結論になりそうでビクビクものである.

なんせ,海外の Inferno ハッカーたち (というか,一人しか知らないけど) は,何かあるとすぐに元の C の方のソースを弄って,直接カーネルハックして機能追加してる感じだしなぁ.たくましい (*´ェ`*)

どうでも良いけど,日本で Inferno/limbo を弄っている人ってのは何人いるのだろう.20 人いない気がする.Plan9 はけっこう多そうだけど.

日本のプログラマ人口イメージ (超偏見)

Perl > VB ≒ PHP (SQL, HTML) > JavaScript >> C > Java > VC++ > C++ >> FORTRAN > COBOL > Python > Ruby >> (業務に使える可能性がある言語の壁) >> Scheme >> Haskell > Lisp >> ML >> (超えられない壁) >> ken C > limbo

となると,仮名漢字変換エンジンとかフロントエンドプロセッサとか方面は絶望的になるのか.だれか limbo に skk かなんかを移植するようなパワーハッカーはいないものか (どのぐらい需要があるんだという罠)

もしたまたま通りかかった limbo-er がいたら,コメント欄で挙手していただけるとうれしい ↓

とりあえず (・∀・)/ 一人目 (見習い limbo-er)




というか,アレか.Inferno 的には,端末とかいう古臭い概念は捨てて,acme 使えってのが本筋なのかもしれない (いまさら) どおりで shell が貧弱すぎるわけだ.たぶん wm/sh とか wm/edit とかはオマケなんだな.acme が本命のエディタであり,shell であるわけだ.

ようやく acme の使い方がわかってきたという.acme 使えば,テキトーな場所に文字列を入力して,中ボタンクリックで評価することになるから,そもそも履歴とか補完とかはあんまり要らなくなるのか.なるほど.

acme ってのは,いわば 「GUI もファイル,shell もファイル,入出力もファイル」という徹底的な抽象化の産物なんだなぁ,きっと.全てがファイルならば,ファイルを編集する acme はエディタでありシステムインタフェースでもあると.歴史的に GUI が後付けの UNIX とかの固定観念に縛られていてはいかんのですよ.

ある意味 CUI とか GUI という区別自体,本質的なものでは無いと言う (shell のような CUI の方が使い易いと感じるのは,単にエクスプローラのような GUI 環境が OS の上に後付けで乗っかってるだけというツギハギのシステムデザインが原因.ネットワークも GUI も完全に OS に統合されている Inferno とはそこが根本的に異なる).

ちなみに,acme は,Windows 版もあったりする (作者は,上でも紹介した人っぽい.この人は,Charon stand alone complex とかも作ってる).

acme stand alone complex: programmer's editor, shell, and user interface

使い方は,テキトーに C ドライブ以下とかに解凍して

>C:\acme\Acme.exe -rC:\acme

とかすれば OK だと思う.まぁ,Windows 版は後付けなので制限が多すぎてほとんど意味は無いんだけど.雰囲気だけでも.



ニョホッ

Infernoって、いつからオープン・ソースになったん?

[2003/10/10] Inferno 4th Edition Preliminary Public Release! のようです.
Plan9/InfernoTB:0CM:10 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!

limbo クックブック : 簡単なダイアログを出す

2007/04/10(火) 11:45:07

一行入力をユーザから受け取る,あるいは,いくつかのボタンの中から選択させる,というシチュエイションは案外多いものです.

以下がコードです (投げやり)


implement testprompt;

include "sys.m";
include "draw.m";
include "dialog.m";

testprompt: module {
init: fn(ctxt: ref Draw->Context, nil: list of string);
};

init(ctxt: ref Draw->Context, nil: list of string) {
sys := load Sys Sys->PATH;
dialog := load Dialog Dialog->PATH;
dialog->init();
msg := dialog->getstring(ctxt, nil, "input line");
button_labels := list of {"yes", "no"};
res := dialog->prompt(ctxt, nil, nil, "title", msg,
0, # default button number
button_labels);
sys->print("pushed : %d\n", res); # 0 ("yes") or 1 ("no") in this sample
}



limbotestprompt.png


getString で出るダイアログに文字列を打ち込んで Enter を押すと,改行までの文字列が返ります.

prompt で出るボタンを押すと,ボタンの番号が返ってきます.

どちらも,第二引数には,ポップアップウィンドウの位置を調整するために,親ウィンドウへの参照を渡してやることができるそうです.nil を渡すと,他のウィンドウと重ならない一番左上の位置に描画される模様.

prompt の方は,何やら文字列で表示するアイコンを指定できるみたいなんだけど,いまいちよくわからない.とりあえず Inferno のソースを漁ると,"error -fg red" とかして,警告のアイコンを出したりする使われ方が多い感じ.
/icons/tk 以下の bit 拡張子が付いたビットマップファイルを,"fax.bit" とかで指定できるみたい (bitmap ファイルのビューワとかを作って見るのも面白いかも).

というか,クックブックでも何でもなくて,単なる何の面白味も無い dialog.m のサンプルコードですが,こんなのすらネット上にはリソースがほとんど無いので.

当面の目標として,Inferno 上で使える,そこそこ高機能の shell と,簡単なインプットメソッドを limbo で作りたいなとか思っていたりするのですが.どちらも作ったことが無いので,全くの未知.おまけに limbo のライブラリのドキュメントが,現状では最低限の man とソースしか無いという現実.

こういうものでも,転がしておけば,誰かの役に立つかもしれないし,立たないかもしれない.
Plan9/InfernoTB:0CM:0 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!

limbo クックブック : プロセス ID を取得する

2007/04/06(金) 11:47:54

Inferno では,プロセスもファイルとして扱われ,/prog 以下に存在する /prog/<pid> がそれです./prog/<pid> 以下には /prog/<pid>/ctl や /prog/<pid>/wait など様々なファイルが存在し,それらのファイルにアクセスすることによって様々なプロセス操作が可能になります.例えば ctl ファイルに kill と書き込むと,プロセスが終了します.

% wm/edit &
% ps | grep edit
% ps | grep Edit
456 456 aloha 0:00.0 alt 88K WmEdit
% echo 'kill' >> /prog/456/ctl
sh: 456 "WmEdit":killed

参考 : Inferno's man PROG(3)

ファイルにアクセスするためには,実行中のプロセスの ID を知る必要があります.これは pctl (Process ConTroL) システムコールの返り値から取得することができます.


implement proc;

include "sys.m";
include "draw.m";

proc: module {
init: fn (nil: ref Draw->Context, nil: list of string);
};

init(nil: ref Draw->Context, nil: list of string) {
sys := load Sys Sys->PATH;
procf := "/prog/" + string sys->pctl(0, nil);
sys->print("%s\n", procf);
procfd := sys->open(procf, sys->OREAD);
if(procfd == nil) {
sys->fprint(sys->fildes(2), "%s: not found [%r]\n", procf);
exit;
}
}


% limbo proc.b
% ./proc
/prog/168

ちなみに,Inferno には,ファイルの close という概念はありません.ファイルもメモリと同様に GC の対象となり,参照している fd (file descriptor) が存在しないファイルは自動的に閉じられます.明示的にファイルを閉じたい場合は,fd に nil を代入すると良いでしょう.

ところで,プロセスから子プロセスを spawn で起動する際,起動したまま親プロセスが終了してしまうと,制御を失った子プロセスがゾンビプロセスになってしまうという問題が起こります.そのため,Linux などでは waitpid システムコールなどにより,子プロセスが終了するまで待機していました.

Inferno の場合は, /prog/<pid>/wait を読むだけで,自動的に親プロセスが wait します.


implement exec;

include "sys.m";
include "draw.m";
include "sh.m";

exec: module {
init: fn (nil: ref Draw->Context, nil: list of string);
};

init (ctxt: ref Draw->Context, argv: list of string) {
sys := load Sys Sys->PATH;
stderr := sys->fildes(2);
if (argv != nil && tl argv != nil) {
cmd := hd tl argv;
args := tl tl argv;
bin := load Command cmd + ".dis";
if(bin == nil)
bin = load Command "/dis/" + cmd + ".dis";
if(bin == nil) {
sys->fprint(stderr, "%s: not found\n", cmd);
exit;
}
pid := sys->pctl(0, nil);
sys->fprint(stderr, "pid: %d\n", pid);
wait := "/prog/" + string pid + "/wait";
waitfd := sys->open(wait, sys->OREAD);
if (waitfd == nil) {
sys->fprint(stderr, "%s: not found [%r]\n", wait);
exit;
}
spawn bin->init(ctxt, cmd::args);
buf := array [256] of byte;
n := sys->read(waitfd, buf, len buf);
sys->fprint(stderr, "done: %s\n", string buf[0:n]);
} else
sys->fprint(stderr, "usage: %s cmd args\n", hd argv);
}


% limbo exec.b
% ./exec charon
pid: 360
# charon 終了
done: 361 "Charon":

wait の他にも,チャネルなど,子プロセスが終了するまで親プロセスをブロックできればなんでも OK です.使える場合はチャネルを使った方がスマートですね.

/appl/cmd/kill.b などを見てみたら,kill も,結局は /prog/<pid>/ctl 開いて,"kill" を fprint しているようです.ps.b なんかも,/prog 以下を全部なめて,/prog/<pid>/status を print しているだけみたい.

わずか 40 行程度なので,これはディレクトリ走査のサンプルとしても良いですな.


implement Ps;

include "sys.m";
include "draw.m";

FD, Dir: import Sys;
Context: import Draw;

Ps: module
{
init: fn(ctxt: ref Context, argv: list of string);
};

sys: Sys;
stderr: ref FD;

init(nil: ref Context, nil: list of string)
{
sys = load Sys Sys->PATH;

stderr = sys->fildes(2);

sys->pctl(Sys->FORKNS, nil);
if(sys->chdir("/prog") < 0){
sys->fprint(stderr, "ps: can't chdir to /prog: %r\n");
raise "fail:no /prog";
}
fd := sys->open(".", sys->OREAD);
if(fd == nil) {
sys->fprint(stderr, "ps: cannot open /prog: %r\n");
raise "fail:no /prog";
}

for(;;) {
(n, d) := sys->dirread(fd);
if(n <= 0){
if(n < 0) {
sys->fprint(stderr, "ps: error reading /prog: %r\n");
raise "fail:error on /prog";
}
break;
}
for(i := 0; i < n; i++)
if(d[i].name[0] >= '0' && d[i].name[0] <= '9')
ps(int d[i].name);
}
}

ps(pid: int)
{
proc := string pid+"/status";
fd := sys->open(proc, sys->OREAD);
if(fd == nil) { # process must have died
# sys->fprint(stderr, "ps: /prog/%s: %r\n", proc);
return;
}
buf := array[128] of byte;
n := sys->read(fd, buf, len buf);
if(n > 0)
sys->print("%s\n", string buf[0:n]);
}


参考 : Inferno's man SYS-PCTL(2)
Plan9/InfernoTB:0CM:0 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!

最近のコメント

リンク

このブログをリンクに追加する

最近のトラックバック

人生の残り日数

日本人男性の平均寿命は 28700日.

RSSフィード

カテゴリー