やってみたら意外と簡単で、足りないところは多々ありますが3日でLispとしてとりあえず動くようになりました。
(実装メモ1日目、2日目、3日目)
以下は今回の実装を体験してわかったこととか感想とかです。
- 静的型システムの言語で動的型システムの言語を書くのはちょっと面倒
- GCがない言語でGCをサポートする言語を書くのは相当面倒そう。
- C++のdynamic_castは注意が必要
- Effective C++ は偉大である
- 偉大な本が示してくれる落とし穴も、実際には自分がはまってから気づくことになって悲しい
- print は再帰的に書くと楽
- read は再帰的に書くと楽
- eval/apply も再帰的に(ry)
- つまり再帰すごい
- レキシカルスコープにおける環境フレームの意味は結構正確に理解できていた。
- applyするときは、 環境なしで実行すればよい (というか関数に渡ってくる前にevalで全部評価された引数が渡されるので環境が不要。合成関数内部ではレキシカルな環境を保存してるのでそいつと引数から新しく作った環境を使う)
- 合成手続きをapplyするときの理解もほとんど間違ってなかった
- eval/applyの相互再帰からlambdaが湧いてくる様子を実体験できたと思う。
- evalまでできてしまえばマクロの実装まで結構楽だった。
- 自分が実装したLisp処理系でマクロが動くのホントに楽しい。 これで何でも書けるぜーって気になる。
- 実際 let* みたいにmacroを重ねたような構文は、構文解析方面からアプローチすると死ぬだろうな。処理系内部で実装するにしても今回MITスタイルdefineのときにやったように、抽象構文木(S式)を変換して再度evalさせるというマクロ的アプローチのほうが楽そう。マクロは偉大である。
- 普通のアプリのプログラミングって、言語自身が何でもできる状態から、目的の仕事に特化させる代わりにどんどん制約を加えていくようなイメージを持ってるんだけど、言語をつくるのは逆に色々覚えさせていく、ある種「究極の育成ゲーム」みたいな感覚になる。楽しい。
- でも言語であっても規模が大きくなると辛さが増しそう・・・ Lispに限らず堅牢で実用的な各種処理系の実装者・保守者の方々はホント偉大だと思う。
課題など
- dynamic_cast を減らした実装へ変更してパフォーマンス比較しときたい。
- union を使った即値で実装してみたい。
- GCも実装してみたい。
- 合成関数のボディ部は作成時に最適化されたS式に変換もできそう (マクロは展開しとくとか変数以外のシンボルはある程度解決しとくとか)
- リードマクロは read にアクセスするポイントを作れば行けそうな気がする。
- ネイティブコードへのコンパイルは未だ謎。違う意味論でS式を eval するんだろうけど具体的な変換が想像つかない。
- 同じくファーストクラスの継続も実現方法がわからん。 やっぱ自前のスタックなりを経由して計算コンテキストを受け渡したりとか? 継続に手が届けば末尾呼び出しの最適化もある程度はナチュラルにできてしまいそうなので興味はあるんだが。
**
今後はとりあえず SICP 4章を読んで自分で答え合わせをしていくつもりです。
4章は実装ゲーらしいので、今回作ったやつを改造しながら読んでいくと面白そう。
0 件のコメント:
コメントを投稿