sweet.js を少し使ってみた。
この 1 週間、寝不足になりながらも毎夜毎夜遅くまで sweet.js というものを触っていた。
このライブラリ端的に言えば、 DSL でマクロを書いてコンパイルすることで JavaScript に変換出来るという優れもの*1。マクロは Scheme の syntax-rules と syntax-case に近いものが実装されている。なので基本的に Common Lisp のマクロぽいことはやり難い*2。mozilla 謹製というところも注目すべきところだと思います。
さてはて、触ってたのはいいんですが疲れました。 anaphora 系のマクロを作ろうとして aif は簡単に書けても awhile や aand がなかなかどうしてうまくいかなくて*3、結局 Scheme ぽい健全なマクロしか書けないので代替案を採用するしかなさそう。というふうにこのページを読んで、納得したので and-let* を書いてみた。
macro ($$let*) { rule { ($($id:ident : $val:expr) (,) ...) { $body ... } } => { (function(){ $(var $id = $val;) ... $body ... }()) } } export ($$let*) macro ($$and_let*) { rule { ($it:ident, $cond_list:expr) { $body ... } } => { (function(){ var result = true; for(var i = 0; i < $cond_list.length; i++){ result = (result && $cond_list[i]); } return $$let*($it:result){ if($it){ $body ... } } }()) } } export ($$and_let*)
マクロこんな感じ。とりあえず、シンプルにパターンだけで書けた。もう少しエラー処理とかしたほうがいいんだけど、そこはまぁ後で*4。
実際に使うとこんな感じ。
var ageRestrict = 20, someone = {name: 'ayato_p', age: 21}; $$and_let*(name, [someone, (someone.age > ageRestrict), someone.name]){ console.log('over 20: ' + name); // over 20: ayato_p }
これが展開された形はこう。
var ageRestrict$40372 = 20, someone$40373 = { name: 'ayato_p', age: 21 }; (function () { var result$40375 = true; for (var i$40376 = 0; i$40376 < [ someone$40373, someone$40373.age > ageRestrict$40372, someone$40373.name ].length; i$40376++) { result$40375 = result$40375 && [ someone$40373, someone$40373.age > ageRestrict$40372, someone$40373.name ][i$40376]; } return function () { var name$40378 = result$40375; if (name$40378) { console.log('over 20: ' + name$40378); } }(); }());
ええ、ええ、美しくないですね。コンパイル後のそれは人の読むものじゃないです。もはやバイナリです。
ちなみにマクロの頭に $$ ってつけているのはなんとなくです。マクロだよーって分かりやすくするために自分の書いたマクロは $$ 頭に付けてスネークケースで書いてます。まぁ気分の問題です。
let* みたいなものはマクロじゃないと書けないかなって気がします。生の JavaScript でもある程度表現力あるので、「ぽい」ものなら書けなくもなさそうですが、マクロで書くと幾分綺麗にまとまりますね。
書いてて思ったのは結構エラーが分かりにくいのとデバッグが容易ではないということですね。あと割と癖があるので文法的なとこで間違うと泣きたくなりますね。 document になっていないようなコーナーケースもそこそこある気が…。で、マクロでコード圧縮するのはいいんですけど、展開したものが結構膨らむ傾向にあるので、小さいモジュールを書く場合とかには不向きかも。
それと忘れてた readtable というものがあってどうもパーサーを自力でイジれるぽい?あー、リードマクロ的な何か。ちょっとあまり触ってないし、ドキュメントが何言ってるのか理解出来なかったので、詳しくは読んでない。
あと、僕が書いた sweet.js のコードの残骸がみたい人は見るといいと思う。もっと触っていこうとは思っているけど、まだちょっと慣れてない。
ayato0211/my-js-module · GitHub