(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

sweet.js を少し使ってみた。

この 1 週間、寝不足になりながらも毎夜毎夜遅くまで sweet.js というものを触っていた。

このライブラリ端的に言えば、 DSL でマクロを書いてコンパイルすることで JavaScript に変換出来るという優れもの*1。マクロは Scheme の syntax-rules と syntax-case に近いものが実装されている。なので基本的に Common Lisp のマクロぽいことはやり難い*2mozilla 謹製というところも注目すべきところだと思います。

さてはて、触ってたのはいいんですが疲れました。 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

*1:??

*2:主に anaphora 系、その代わり名前の衝突は気にしなくていいので気が楽ですけどね。ちなみにその健全さが必要なければ壊すことも可能なので、 aif 程度なら簡単に実装出来ます。

*3:中途半端に健全さを壊すことが出来るので、作れるかなーと思ったら失敗したというだけで、もしかしたら aand も作れるかも??分からない。

*4:後でとはいつなのか