nfunato
舟渡(nfunato at acm dot org) と申します。
本業はエンベディッドシステムの開発(だった)ですが、最近すっかり 御無沙汰のロートルです。
Cコンパイラのリンカーを書いてCを覚え、Blue Book のPart Fourを 読んでSmalltalk に馴染み、PCL(Portable Common Loops)のソースを 読みながらCommonLispを覚え、Orbit(T compiler)の論文とoaklisp の ソースを読みながらScheme を覚え、…といった言語遍歴です。
どうにも実装から入るという悪しきめぐりあわせです(自称「変格派」)。
Macintosh CL とoaklisp が一番のお気に入りでした。といっても、 そのまま本業のアウトプットにはならないので、専らプロトタイプか 内製ツールとしてですが。
(2009/09/26 22:44:57 PDT)
(Shiro 2009/09/25 プログラミング言語の進化)より:
全くShiroさんに同意ですね。
あと、言語の進化がベストプラクティスをサポートするためにあるのはそうなんでしょうが、プログラマとしては特定の言語がプログラマの考え方を強制する罠に陥らないように注意が必要なんでしょう。
言語に対しては、ShiroさんのMELの話(2004/06/26)にあったように「……が出来ない」という風にはして欲しくないわけですが、例えば「エラー処理必ずしも大域脱出にあらず」という考え方は、プリミティブなCでは(面倒であっても)実現可能なのが、C++のように言語サポートに含まれて ライブラリが追従してしまうことで、逆に難しくなってしまうという現象が起こるかもしれないのです。
気軽に言語の方を変えるには Lispはお勧めですが、頭の方も柔軟に保っとかないといけません。
なんか、久しぶりに書きたくなってしまいました。
"Lisp is a programmable programming language."
- John Foderaro, CACM, September 1991
(2009/04/24 19:47:12 PDT) PLOT: Programming Language for Old Timers by David Moon, 2006-2008.
introductionによれば、
Programming Language for Old Timers (PLOT) is a new dialect of Lisp designed by Dave Moon in February 2006, and thoroughly revised and simplified November 2007 and March 2008. I have been developing PLOT as a hobby, with the idea of for once having a programming language which does everything the right way.
ということなので、Lisp:読み物に
nfunato: http://people.csail.mit.edu/jrb/Projects/dexprs.pdf にもDylanマクロシステムの紹介があります(jrb氏はJava Syntax Extenderなんてのも書いてます)。昔話で恐縮ですが、Acknowledgementsに名を出すDavid Moonの書いたマクロシステムのドラフトは、1994頃 ftp.apple.com のトップにさりげなく置いてあって、もっと細かくASTの操作について書いてました。arcへのコメントでも、Moon氏はS式反対の立場なんですよね。
と書いたとき(2006/09/28)には、既に原型があったわけですね。
in a right way でなく、the right way というところに、mit approach のコダワリを感じてしまう。
最初に出てくる実装が良いFFIを持っているといいですね。言語仕様がすごく大事なことは承知のうえで、インターネット時代の昨今は、実装の数よりも良いライブラリが付くかどうか重要だし、枯れたライブラリにはCやFortranで書かれているものもあるから。
最近、身の周りのことをするときにPythonを使っているが、それはscipy、numpy、matplotlibを日常使わせてもらっていることによるもので、言語の"仕様"に不満がないわけではないのである。
32-bit windows で使える Clozure CLも出たし、Old Timer Lisperには春から縁起がいい?
(2009/01/03 16:52:48 PST)
帰省中、つかのまにネットサーフィンしていて、Eliot Miranda氏のblogに行き着いた。お名前は存じあげていたが、このような情報(jit compilerを含むcroquetのVM?)に触れられるようになったのは興味深い。既に自分の手におさまる話題ではないと知りつつ、文庫本の代わりに「あとで詠む」と思って、一部を保存する。しばらくネット接続もままならないので楽しめそう。
それにしても、Miranda氏にせよ Weinreb氏にせよ、私と変わらない世代だと思うが、ある種の尖がったソフトウェア領域でのアメリカの強みは、こういったノウハウを蓄積しながら前線に居続ける人の多さにあるのではないか。
(2008/07/25 08:37:45 PDT)
Scheme:なぜSchemeにはreturnが無いのかより:
本題とは外れるので、こちらにて。
実際、Cのsetjmp/longjmpと継続とは非常に近い関係がある。ただ、Schemeの場合、オブジェクトのエクステント(有効範囲)は原則として無限だ。 setjmpで捕まえたjmp_bufは、それより下位の関数呼び出しの中でしか使えないけれども、 Schemeの継続にはそういう制限がない。まあ、言ってしまえば違いはそれだけさ。
"それより下位の関数呼び出しの中でしか使えない" というのは意味論の上での要請で、実際には 巻き戻されて無効になった筈の jmp_bufにlongjmp することが出来ます :-)
C言語とsetjmp/longjmpだけを使って non-preemptive thread を作ったことがありました。初期化ルーチンの中で、何重にも再帰呼び出しをしていって、ある回数の呼び出しごとに setjmp を呼び出して、stack areaと同時に jmp_buf というか TCB(Task Control Block)を確保していきます。所定の数のTCBを確保したら、一番最初に確保したjmp_bufまでlongjmpして、初期化ルーチンは終わり。以後は task schedulerを動作させればよい。
task schedulerは、巻き戻されていようがいまいが、特定のTCBのjmp_bufにlongjmpすることで、その context に switch 出来ます。
厳密にはOS依存な筈なんですけどね。例えば、巻き戻したstack segmentのpage割り当てが適当なタイミングで解放される場合は、core dump します。また、コーディングをする上では、volatile とか使用するなどして、種々のコンパイラの最適化を避ける必要があった。が、某Unixと複数のバージョンのgccで動作していました。
"初期化ルーチンやcontext switch"の部分をそんな下品な方法で行う必要は何処にも無いんだが、真の目的は 組み込み機器で動作する embedded preemptive kernel の初期デバッグをホスト上で行うことだったので、ターゲットでは別コードに置き換えられる上記の部分を一番短く作る方法でした。
もちろん、動的に継続を生成したり、closure変数を作ったりすることは簡単ではない。そんなことをしたら、Lisp:GreenspunsTenthRuleになってしまうに違いない。
- Shiro(2008/07/25 11:01:32 PDT): 要するにスタックフレームが上書きされていなければ 上位だろうが下位だろうがlongjmpできるわけでではありますが。再帰が深くなりすぎて 別のスレッドのスタックエリアを上書きしちゃうなんてケースは運用で回避していた のでしょうか。
- nfunato(2008/07/25 16:26:29 PDT): いえ、組み込み(firmware)なんで、動かすプログラムの挙動は何処かで確定するんです。ホストで動作させる段階ではカバレージを上げることが課題で、スタック消費量の見積もりはまだ重要ではないので、保守的に見積もっておきます。
上記のケースはかなり規模が小さいfirmで、(ハードに対する)ソフトの分担が確定する前から実行速度を見積もっていたような記憶がありますから、カバレージ云々という頃は、消費量も相当分かっている段階ですけどね。
Schemeの話をしているところなのに感度が鈍かったですね。":-)" ということでご勘弁を。
(2008/03/29 17:53:55 PDT)
Daniel Weinreb 氏の blogに、 CL condition systemについてのpost(What Conditions (Exceptions) are Really About) があり、下記を含む Dave Moon氏のreplyがあったりして、ゆるゆると議論が行われている。
I believe the distinction (between condition and error) was intended to be that the function that signals an error is incapable of continuing execution, while the function that signals a non-error condition is capable of continuing execution if the exception handler does not unwind the stack. This depends on Common Lisp’s separation of exception signalling from stack unwinding, two distinct concepts that other languages have conflated. However, the Common Lisp standard is not entirely consistent about this, so it is easy to become confused about the intended model.
CLのように exception signaling と stack unwinding を分離したエラー処理機構には、signaler の stackで caller側で定義された handlerの実行を可能にする function closure が 欲しくなる。
実用上は、cacading signaling や error handling による 再帰呼び出しの過程でもスタックをあまり消費しないための proper tail recursion や、プログラミングし易いように application oriented な error type を mixin する機能も 欲しいかもしれない。
GC に付随して、closure が言語機能として一般的になってきても、上記の分離が一般的になってきたように見えないのは、両者の使い分けがあまり有用ではないと思われているのだろうか? つまり、実行時に起こり得る "unusual condition" で 継続可能なものというのが、それほどないのだろうか?
いずれにしても、両者の使い分けは 難しいものであるらしい。
私自身は、CL condition system の仕様を知っていたおかげで、メモリの少ない組み込み機器に、setjmp と longjmp に皮を被せた アプリケーション依存な special adhoc error system を組み込んで、コーディングを随分「楽」、つまり小さくかつ不具合の出にくいものにできた経験がある。
また、KCL (AKCL + ilisp) で プログラムしていたときは、デバッグ時の関数定義は、専ら break-loop の中でしていた。人間handler ですね。KCLインタプリタのデバッガは、いい意味でナイーブで、手を入れるのも容易でした。
続きがありそうなので、ちょっと期待して見守っています。
毎度 scheme な話でなくてすみません。
(2007/12/07 02:51:09 PST)
sasagawa:思いつき?(Lisp処理系の作成)より。
中西正和先生は、1つならずLisp処理系を作っておられるでしょうが、私がお世話になったのは、Apple Lisp (Apple II Lispだったかも ?)。これは6502アセンブラで書いてあったということになります。同名の記事が古いbit誌に出ており、大学の研究室にあったLisp 1.5のマニュアルと共に読みましたが、だいたい同じような仕様でした。Joy Stickで操作するスクリーンエディタが組み込まれていました。
記事に、I/Oの部分が完成するとだいたいLispは出来たような気になる、って書いてあったようなおぼろげな記憶があります。
I/Oが大変という観点からいうと、とりあえずLispを動かすのに最も手が抜ける言語は、Lisp自身ということになりますかね (最近は良いParser Generatorもあるから、ちょっと嘘臭いか)。上に書いてますが、私が読んだ TやOaklispは、この手合いでした。
Common Lispを含めて、しっかりしたnamespace機能のある Lisp言語族なら、てっとり早く動かしてみるところから、序々に機能を追加する形に持っていけそうです。 仕様がしっかりしたものというと、CLあたりになってしまい、その仕様に馴染むこと自体が大変なのが難点 :-)
Oaklisp(第1回だかのOOPSLAに論文が出てます)も、Symbolics上でbootstrapしていって、ターゲットであるMacintosh上の仮想マシンで動くようにして、最終形のVMコードコンパイラ込みで単独マシン上でセルフビルド可能な形にしたようです。
今も、昔のソースを著者の一人(アイルランドの大学の先生)が公開していますが、2人の著者がソースコードのコメント上で会話しているのが楽しかったですね。Hey Kevin ! とかいう感じで…
- sasagawa?:(2007/12/07 04:00:05 PST) 私は昭和58年頃にLispに出会いました。中西先生の「Lisp入門」を買ったのがこの頃です。AppleⅡで動くLispでガベージコレクションをディスプレイで見ることができるという話を聞いたのは中村正三郎さんのエッセーだったと思います。残念ながら私はAppleを持っていませんでした。Bit1997年1月号の「バグを出さない」という記事で中西先生の書いていたことが記憶に残っていました。Lisp1.5のプログラマーズマニュアルを頼りにLispシステムを作ったという話です。ずっと、自分でLisp処理系を書くという願望を持っていたのですがChezSchemeやGaucheといった超優秀な処理系を知ってしまい意欲喪失してしまっていました。インターフェースarchive NO.5「特集Lispプログラミング」のCommonLispで書かれたSchemeインタプリタ辺りから再挑戦してみたい気持ちです。
- nfunato:(2007/12/07 18:00:12 PST) なるほど、Apple Lispは Lisp1.5のマニュアルを元に作られたのでしたか! 20数年来の謎 :-) が解けた気がします。
CLで書かれたSchemeとしては、PseudoSchemeも有名ですね。更新の頻度はわからないですが、2004年以降も場所を変えて公開されています。その新しい版のページによると、初期のPseudoSchemeでScheme48をbootstrapしたんだそうです。常道なんでしょうね。
全部は読んでないですが、dispatch macroを使った #Tや#Fの作り方は、ここで学んだ覚えがあります(15年くらい前の話)。
ChezもGaucheも素晴らしい処理系です。Lisperにはマクロがあるので、Runtime Systemの特徴に拘らない限り、俺Lispを作る必要は少ないと思いますが、趣味の世界は別ですよね。物を作る楽しさは格別です。
(2007/10/12 05:33:39 PDT)
最近、info-MCL ML のトラヒックが上がっていると思っていたら、何とMCL PPCは5.2からopen source になるんだそうです (まだ ML archiveには出てないけど)。
むぅ…
(2007/10/12 16:09:30 PDT)
12日の Planet Lisp でもここがポイントされていました。
MCL was a cutting-edge GUI Common Lisp environment for the Mac. It is second only to Lisp Machine environments at inspiring its aging users to sneer at the idiot Lispers who think command-line tools and terminal-based editors and Tk bindings are cool.
68kの頃からでも使えるのは、GUIの下の interactive native code compiler の存在が大きいんではないかと思っているのですが、cmuclのhemlockとかと比べると、MCLはずっと軽快なんですよね、これが。(speed を考えるときに、memory footprintとかも考えてしまうのが aging user の証しかもしれないんだが…)
(2007/10/16 08:24:09 PDT)
Archiveは、こっちに移ってたみたいです。10月が大変なことになっている。
(2007/09/18 06:21:12 PDT)
末尾再帰とループ問題
これを拝見するまで、Common Lispのloopマクロがtail recursiveでない理由に気づかないなんてどうかしてますね。
それにしても、special binding という open end な仕様を、何の制約もなくシステムが使いまくっているってことで、threadを重たくして stack groupを3本スタックにするとか、condition system に (Dylanと違って) 壁を作るとか、実装や仕様に散々な影響があるんですなぁ。。
Schemeでは、この辺は綺麗に成熟していくんでしょうか。
(2007/05/11 09:34:33 PDT)
Method Cache Hacking (← まつもとさんのところ)
こういうの好きです。
組み込み稼業でしたので、それに加えて、こういうコンパイラがクロスでできないか考えてしまう。Java Beansが出る前ですが、Apple Dylanはターゲット実行プロセスにattach/dettach可能なIDEのデモをWWDCでやっていました。そんでもってついでにThumbをターゲットにしてしまうなんていかがでしょう。S60にPythonが載るご時世ですし、PC上のVMもThumb Simulatorにするのです。PPCや純正ARMに比べて、レジスタは少ないし色気に欠けるかもしれないが、ケータイの市場は大きいのですよ。既にheteroなmulti-core(DSPやUMTS)ですから、そのうちアプリもmulti-core ! (ほんまかいな)
SchemeはCLに比べて仕様が未完故に、この辺りに可能性があるように感じます。CLのpackage仕様を必須とすると、重装備のセルフ環境が想定されちゃいますから。(しかし、年配者に時間はないのである…)
(2007/07/20 07:37:49 PDT)
5/23リリースのLLVM2.0 (← やはり まつもとさんのところ)には、Thumbサポートが加わったんですね。いつもお世話になってます。1年程ごぶさたのC++を眺めてみるか。(しかし、でっかいシステムだ…)
(2006/09/09 09:26:03 PDT)
PCLって、Portable Common Loopsではなくって、最近はPractical Common Lisp のことだったとは。知らんかった。
Another classic macro-writing MACRO: ONCE-ONLY
これは、どっちのPCLにも出てて、十分Schemeでも使えそう。ただ、gensymに慣れた自分には、どうやってhygienicに書くのかパッと浮かばない…
(2006/08/04 22:11:09 PDT)
お盆休みを前に、部屋の本を*本当に*久しぶりに整理していたら、本棚の奥からAMOPやCompiling with Continuations (ちなみに 私はこれでSMLを覚えました :-)、Simon Peyton Jonesの本なんかに混じって、Dylan -- An object-oriented dynamic language (1992)というのが出て来ました。
ごれは、94年くらいにJavaの攻勢にあってAlgol syntaxに戦略転換する前の、S式構文時代のDylanのReference本です。
ついパラパラと眺めています。
Larry Teslerの前文に「欲しいものを探していて Eiffel, Self, Beta, Oaklispなどは興味深かったが、我々の要求を満たさなかった」などと書いてあって、当時Gaucheが存在していたら… などと思わず考えてしまいました。
ところで、Dylanでは特定のcollection型に対して6つのメソッドを定義すれば(iteration protocol)、CLtL2のloopマクロに相当するものが使えるというストーリーでした。なおかつ、根がOOなんで、CLみたいにlistとarrayでprefix keywordが異なったりしないんです。
Shiroさんの以前の話(2006/01/10 23:54:26 PST)に、CLのloopマクロは副作用の悪を教えるSteeleの親心とありましたが、公式にtail recursiveでないからですかね。Dylanのは、ちゃんと再帰のsemanticsになってました。
loopマクロはある種パターン化を導入することで、読み易さを向上させると思うんだけど、Dylanみたいなことしたらコンパイラの最適化は面倒になるのかしら。。。
(2006/08/05 17:37:45 PDT)
自己フォローです。Rich Taubeさんが、Common Musicの中で CL loop macro を実装されているようですね。
http://article.gmane.org/gmane.lisp.scheme.gauche/583
> If you come from a common lisp backgound its pretty > indispensible but of course it will probably make a real schemer puke.
言った手前、自分でも使ってみようと思います。とりあえず、自分が Real Schemer でないことがわかりました (-o-)。という以前に自分でもの(コード)を創らねばね。
(2004/03/10 02:36:18 JST)
この一年ばかり自分では殆どプログラムしてない状態ですが、数日前からGaucheを使わせていただいて、リハビリに励んでいます。
ところで使っているうちに、CommonLispのdestructuring-bind 相当の機能が何度か恋しくなったのですが、これって今のSchemeやGaucheにはないものなんでしょうか?
いにしえのPCL(CLtL1時代!)やoaklispには、わざわざ自前で定義してありましたから、便利に感じる人はいると思うんですが、ひょっとして、何でも加えるのはSchemeの思想に反する?ということで、採用されていないのでしょうか?
私が見落としているだけかもしれないのと、他力本願でWishlistに書いてしまうのも正しくないような気がするので、どなたかこっそり教えてください (^^;)
- Shiro(2004/03/13 00:58:49 PST): Schemeでは何らかの外部のパターンマッチライブラリを 使う方向が多いですかね。Biglooは自前でmatcherを持っていますが。 pure Schemeで書かれたmatcherではAndrew Wrightのものが有名です。 SLIBに入ってなかったかな。Gaucheでコアに入れていないのは、 「その手の仕事をこなすのに、destructuring-bindが最適な形なのか」を まだちゃんと考えていないからです。 (特に、ユーザ定義のオブジェクトをどう自然に組み込むかってあたり)。 そのへん、良い案があればお聞かせ下さい。
- nfunato(2004/03/16 02:10:00 JST): CLほどにはSchemeの世界に接してなかったので、Andrew Wrightのmatcher(MATCH)は知りませんでした。Scheme FAQにもちゃんと載ってますね。ありがとうございました。
Schemeでのlist destructurerの標準記法を知りたかったわけですが、ドキュメントを見てると、MATCHがパターンマッチの対象にしているのは、list以外に vector、structure、structureのfield、setter、getter、patternのand or notとか相当色々ありますね。他にもhashtableとか、classのインスタンスとか、いくらでもできそうです。
listに対してdestructuring-bindを使うメリットは、(car やcdrに比べて)一度に沢山の変数をバインドできるので、コードが小さくなって一覧性が良くなるのと、パターンの形式自体が宣言のように見れるので、それ自体にドキュメント性出てくることと思いますが、
他方、デメリットとしては、構造データをパスする方とパスされる方がデータの構造知識を共有するので、保守フェーズになると、構造を変更したときに片方を直し忘れてハマることがあります。MLのようにstatic type checkerがあると、事情は変わるんですが。
で、私がdestructuring-bindを使うときは、データパターン自体に意味があって、パスする方かパスされる方のいずれかに強く依存しないパターンに限定して使うように意識してました。典型的には、リスト形式で表した簡単な言語の処理系くらいです。例えば、プロトコル仕様からのプロトコル処理プログラムの生成ツール(stub compiler)の記述なんかに使いました。
「特に、ユーザ定義のオブジェクトをどう自然に組み込むかってあたり」ですが、上記のようなことから、(インスタンスを対象に考える場合)field、getter、setterというpublicインタフェースをどう書くかということになって、with-slots に毛の生えたようなものになりそうです。色気のない答えですいません。
結局、アクセスインタフェースって型なんで。MATCHにあるand/or/not patternは、型としてみるとかなり意欲的というか大胆で、バランス悪くないかなー、と思いました。
あと、完全でなくても何らかの型チェックのようなサービスを処理系に期待できるようにしたいですね。MATCH自体、そもそもsoft typingの研究にともなって作ったものだそうですね。未だドキュメントは見てませんが、type check predicateでも埋め込んで、コンパイラに期待でしょうか?。MCLのコンパイラとか結構いいんですが、with-slots とかmacroexpandすると(slot-value obj slot-name-symbol)の山で、slot-name-symbolが定数であっても、その正当性とかさすがにチェックしてくれてなかったです。
MLでも、pattern 記述に絡んだrecordの構文は、type systemをどうするかで相当競争されてた部分と理解してます。もちろんSchemeをMLと同列に言ってもしょうがないですが。(ここまで書いてきて、ふと思ったんですが、書き易い/読み易い構文のことを言われたんでしたっけ ;o )
長くなりまして、すいません。
PS1.
MATCHの消息は以下から得られました。
ftp://ftp.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/scheme/code/match/ http://list.cs.brown.edu/pipermail/plt-scheme/2003-January/001722.html http://sol.cs.wcu.edu/~bhauman/scheme/pattern.php
1つめのがオリジナルのようです。3番目には結構いろんなバージョンがあって、個々の内容はよく掴んでないんですが、hygienic macro で書き直してあって結構オリジナルに比べて短いので、あとで眺めてみようと思ってます。
PS2.
Gauche:スロットアクセスで議論しておられるような (ref (ref obj 'slot1) 'slot2) は、上記のstub compilerでは、左辺値でも右辺値でも (-> obj 'slot1 'slot2) あるいは obj.slot1.slot2 と書けるようにしてました。左辺値の場合、具体的に書くと(set! (-> obj 'slot1 'slot2) val) と (set! obj.slot1.slot2 val) と ((setter ->) val obj 'slot1 'slot2) は みんな同じ、というものです。
dotで区切る記法はDylanからのパクリですが、対象言語のこの部分が同じ構文だったこともあります。それ以外の部分はS式ベースです。
(2003/03/20 23:48:37 PST)
実は、最初フリーフォーマットの日本語の文章を含む入力デー タをWiki+dbm に放り込んでおいて、後からPostgresに持っていく、 なんてツールを書くのに WiLiKi やGauche が使えないか考えている ので、ここに出てきました。
最終的にはRDBが不適というわけではなく、ひとまとまりのデータも 数100件くらいなんだが、データを入力している間は正確なスキーマ の細部を決定しにくい(入力データを分析してみて分かる部分がある)、 という感じです。
まだ思いつきの段階で、具体的に問題にぶつかったとかいう訳では ないのですが、似たような経験や事例を御存知の方はおられるで しょうか?