Let's start Scheme

2011-04-28

letrec-syntaxにはまる

現在Racketのr6rs test suiteをパスしようとしているのだが、
(実は既に絶対にパスしないテストがあったり、テストライブラリをいじらないとだめだったりとかなので、すでのR6RSとは名のれないというのは確定なのだが)、
こんな初歩的なテストに引っかかってる。
(letrec-syntax ((my-or (syntax-rules ()
                         ((my-or) #f)
                         ((my-or e) e)
                         ((my-or e1 e2 ...)
                          (let ((temp e1))
                            (if temp
                                temp
                                (my-or e2 ...)))))))
  ;; 展開後ifが関数呼び出しに、マクロ内のtempが
  ;; すべてletで束縛しているものになる
  (let ((x #f)
        (y 7)
        (temp 8)
        (let odd?)
        (if even?))
    (my-or x
           (let temp)
           (if y)
           y)))
何がだめかというとsyntax-rulesの健全性がletrec-syntax内では崩れているということ。
逆に言えば、letrec-syntaxであればsyntax-rulesを使って伝統的マクロみたいなことができる、のだがまぁそれではだめなわけで。

理由は実は分かっている。
syntax-rulesはexplicit renamingで実装しているのだが、そのrenameが問題になっている。どういうことかというと、renameにはコンパイル時環境を使用してシンボルを識別子に変換しているのだが、その環境が問題になっている。letで束縛した束縛変数の情報がコンパイル時環境の中に含まれているため、マクロ展開時にシンボルの探索を行うと束縛変数が引っかかってくるのである。
っで、renameでは渡された引数がシンボルだった場合、現時点での環境を使用して識別子を生成するためおかしなことになるのである。

じゃあ、どう解決するか。
あんまりいい案がないのだが、syntax-rulesはパターン変数以外は外部と干渉しないことが分かっているので、パターンの中にあるパターン変数以外のシンボルをあらかじめ環境の中に入れてしまえばとりあえず回避できそうな気がする。
問題は、マクロ変換器がコンパイル時環境を直接触れないことか。
さてどうしたものか。

2011-04-20

自由パターン変数

とでも呼べばいいのか?
とりあえず、そのようなものに苦しんでいる。

syntax-caseの実装をライブラリではなく、builtinな実装に切り替えた。(可能ならライブラリにしたいが、今のところ不可能そうなので)
っで、(何度となくぶつかったが)ぶつかった壁がこれ。
こんなコードが怒られる。
(syntax-case
  (list (f (syntax c2) (syntax (c3 ...))))
  ()
  ((rest)
   (let ()
     (syntax-case
       c1
       (=>)
       ((e0)
        (print (syntax rest))
        (syntax (let ((t e0)) (if t t rest))))
       ((e0 => e1)
        (syntax (let ((t e0)) (if t (e1 t) rest))))
       ((e0 e1 e2 ...)
        (print (syntax rest))
        (syntax (if e0 (begin e1 e2 ...) rest)))))))
これはR6RSの何章かにあったcondのsyntax-case版実装を走らせて、途中経過をダンプしたもの。
何がまずいかというと、restが外側のsyntax-caseでパターン変数として現れているのだが、現状の実装では内側のsyntax-caseから外側のsyntax-caseのパターン変数は見えない。

現状の実装としては、
syntax-caseが現れたら、それのパターンとそのパターンの情報、出力部分とフェンダーをクロージャー化したものをmatch-syntax-caseという関数に渡して返している。っで、それがもう一回S式→内部表現の関数に渡されるという感じ。
自分でもここまではすっきりかけたなぁと自画自賛していたのだが(まぁ、Ypsilonの実装からかなり盗んだからだが・・・)、この自由パターン変数が現れてちょっと(だいぶ)躓いている。
上記にも書いたが、パターンのメタ情報はmatch-syntax-caseに渡しているため、どこかから取り出すということができない。この際、Ypsilonでは.varsというトップレベルの変数に入れて、実際に置き換えを行う際にテンプレートの変数が自由パターン変数かどうかをチェックしている。ように見える。
現状、そのような変数を導入してないので、どうしたものかということになるのだが、どうしたものか。

2011-04-15

今更ながらにR6RSのライブラリについて

R6RSのライブラリについて大きな勘違いをしていたかもしれないということに気づいた。

現状Sagittariusにおけるライブラリの扱いはfirst class objectになっているのだが、これが元でsyntax-caseの実装が難航している感がある。
どういうことか?
僕がR6RSのライブラリを正しく理解しているかは分からないが、このライブラリシステム、Javaのクラスとかパッケージの感じではなくて、それ自体がマクロの一部として扱われた方がマクロを実装する際に煩雑さが消える気がしている。

実はこうではないかと思っている案
(library (something)
    (export do-something)
    (import (rnrs (6)))
  (define (print . args)
     (for-each (lambda (arg)
                  (display arg))
               args)
     (newline))
  (define (do-something . args)
     (print args)))

(import (something))
(do-something 1 2 3)
ライブラリがマクロであると考えると、これはこんな感じで展開されるはず
;; #: プレフィックスはrenameの結果とする
;; このプレフィックスはユーザが指定できないようR6RSでレキシカルエラーになるものがよい
;; print -> #:print
(define (#:print . args)
  ...) ;; 中身は一緒
;; do-somethingはexportされているのと、
;; 呼び出し元でprefixもしくはrenameで変更されていないのでこのまま。
(define (do-something . args)
  ...) ;; 中身は一緒
;; メインのプログラム
(do-something 1 2 3)
こう考えれば、ライブラリは展開時(読み込みとはもはや呼べないので敢えてこう言う)にrenameできるので、名前の衝突はなくなるし、C/C++のincludeより多少高等ではあるが、最終的に実行単位は1ファイルということになる。
また、R6RSでは定義の書き換えが禁止されているので(あんまりこれに追従してる処理系は多くないけど)importされた定義が書き換えられるということはない。

と、こう考えると、なぜimportレベルが与えられたのか分かる気がする。つまり、ライブラリを展開する際に先にimportしておかなければならないライブラリがどうしても必要になるからだ。たとえばsyntax-case内で使用される手続き。
これらの手続きは展開時に実行される必要があるので、import時にコンパイラもしくは実行する何かに教えてやる必要がある。曰く、この手続きはマクロ展開時に*実行*してね、と。単純に考えれば、マクロ展開器はevalを使う必要が出てくるということになる。
そもそも、importレベルはsyntax-caseのために導入されたのじゃないかと思うが。個人的にはcondition systemと一緒で言語仕様から切っても切れないものをライブラリと呼んではいけない気がする。evalは一応ライブラリか。しかし、Primitiveでないと実装できないようなものをライブラリと呼んでいいものか。
(error、とかassertion-violationなんてもろCondition Systemに依存してるくせに言語仕様の方に入ってるしなぁ。どうよ?)

さて、この考え方が正しいかどうかは別として(いろんなR6RSの処理系(といってもmosh、Ypsilon、petiteの3つだが)を見てると正しそうではあるが)、この方法で実装するとなると、現状のSagittariusを大幅に変える必要がでてくる。
そもそもライブラリなんていらんかったんだってことになるからだ。
ここまで作ってやり直しになるとさすがに凹むので、別の解を見つけたいところではあるが、いい案が思い浮かばない。

2011-04-14

段々

無理ゲーになってきた感がある。> syntax-caseの実装

とりあえず考えをまとめる。
まずは現状の問題点。

  • 展開結果にer-macro-transformarが使えない。
    • renamingに環境が必要だが、syntax-caseがネストした場合どうする?
  • 地味にsyntaxの方が問題になる
    • syntaxが実際のtemplateを書き出すのだが、別にした場合に上記のrenamingの問題が発生する
  • (syntax-case (list e0 ...) ...)というsyntax-case
    • マッチ対象が展開されている必要がある。
      • 展開をどうする?
    • マッチ対象のコンパイル時環境をどうするか。
      • 一発で展開すると環境が捕捉できない
      • lazy(コンパイラに任せる)にするとどうなる?
  • その他、自分の力不足
    • 正直これが一番大きい・・・
まぁ、まだいくつかありそうだがとりあえずこれくらいで。
4つ目はいかんともしがたいとして、どうしようかね。

現状挑戦したのが展開時に中に含まれるマクロを全部展開しようとしたのだが、上手くいかなかった。
問題はsyntaxで、templateの展開が上手くいかない。
単純なのはいいのだけど、with-syntaxを使ったのとか、別のマクロがネストしててそのなかで構文オブジェクトを返そうとすると上手くいかない。
(単に展開のやり方がまずいのだろうけど、ごちゃごちゃしてきて理解の範疇を超えた・・・)

煩雑になるのは望むところではないのと、ライブラリがコンパイラのついて知っていないといけないというのは気持ち悪いので、syntax-case展開時にマクロを検知、展開するのはやめる方向にしよう。

問題はsyntaxが持つtemplateをどうするかだ。
後にしよう・・・

syntax-case再び

RacketのR6RS Test Suiteを導入しようと思ったらテストを実行するのにレコードが必要で、レコードはdefine-record-typeなんてので定義されてて、その定義にはsyntax-caseが必要っぽいので再びチャレンジすることにした。
(長い一文だ)

とりあえず気づいたことというか、これはひどいなぁと思ったことについて。
R6RSの仕様書的にはsyntax-caseはライブラリであると明記されているのに、コンパイラにマクロ展開のタイミングを強制すること。たとえばこんなコード。
(define-syntax test
  (lambda (x)
    (define (hoge x) (syntax-case x () ...))  ;; (2)
    (syntax-case x () ;; (1)
      ((_ x)
       (hoge #'x)))))
これで何がおきてほしいかというと、(1)のsyntax-caseはxがパターンマッチにマッチしたら、(2)のdefineを呼ぶ必要がある。でhogeが構文オブジェクトを返して、コンパイラはその構文オブジェクトをコンパイルする。
でも、hogeを呼び出すためにはコンパイラがすでに内部defineをコンパイルしている必要がある。

あぁ、でももう少し考えればいけそうな気がしてきたなぁ。
問題は現状では構文オブジェクトを返してもコンパイラはそいつをコンパイルしてくれないことか。

ちょっと気づいたことメモ。
(syntax x)とした際に、環境を捕捉する必要がありそうだ。
結局上の式は、
(define-syntax test
  (lambda (x)
    (define (hoge x) (let ((expr x)) ...)) ;; something
    (let ((expr x))
      (if (match? x)
          (hoge (syntax x))))))
こんな感じに展開されればいいのか。(まぁ、実際にはhogeはletrecな何かだろうけど)

2011-04-12

custom port実装中

とりあえずいろいろ見ながら実装しているのだが、custom-textual-output-portの実装の違いというか、いいのかこれ?っていうのを発見。
たとえばこんなコード
(import (rnrs)
 (rnrs mutable-strings (6)))
(define (print . args)
  (for-each (lambda (arg)
       (display arg))
     args)
  (newline))

(define (custom-read! bv start count)
  (string-set! bv 0 #\a)
  1)

(define (custom-write! bv start count)
  (print bv)
  1)

(define cp (make-custom-textual-output-port "id" custom-write! #f #f #f))
(print (put-string cp "string"))
(close-port cp)
これをmosh, Ypsilon, Petite Chez Schemeに食わせてみる
mosh:
s
t
r
i
n
g
#

Ypsilon:
string
tring
ring
ing
ng
g
#

Petite Chez Scheme:
#
string

string

string

string

string

string

ちなみにPetiteは最後のclose-portがないと「string」が出力されない。ちなみに、write!のプロシージャーに渡されたstart、countもそれぞれバラバラで、moshは固定値「0,1」、Ypsilonはstartが0固定で後はsubstringしたかのような長さ、Petiteはstartとcountの合計値が文字列の長さになるようになってた。
(日本語が下手なので、詳しくは動かしてください)
これinput-portで作ってもそれぞれ実装がバラバラで、正直なんだこれ?状態。

いまいち使いどころが分からないし、とりあえず簡単そうな実装にしてしまおう。

2011-04-07

Google Chrome

For some reason, my firefox 3 and 4 didn't work well. I got horrible internet connection with it, such as google.com could be seen but not hatena.co.jp. First I thought it was because of my laptop, so I actually disassembled it and cleaned it. But unfortunately it wasn't it. Then I installed IE8 which I really didn't want to, and I really wish that was it. Well, as you can see it wasn't it, *AGAIN*.

I didn't have any idea what was wrong, I've even uninstalled my AVG which is a free virus software because I thought it has own firewall, however it didn't change any thing. Actually, I got less memory consuming as a result. But I'm kind of chicken so I've already installed new anti virus software, AVAST!. It's light and less memory than AVG so far I like it.

When I installed it, it recommended to install Google Chrome. Frankly, I've been avoiding to install it, because I felt like Google steal my private information such as the history of my browsing. I don't want them to know my porn site history XD. Just joking.

So far, it's pretty good. It's fast as they say and its style is really simple. One thing I have a problem with this. It can't import my bookmark from firefox. I think this is because my firefox's profile is broken. Does any body know how to fix it?

2011-04-01

4月1日

世間ではエイプリルフールだが、僕にとってはちょっと違う意味を持つ日。
某マイミクは同じ意味の日を「独立記念日」と名づけているが、僕はもともと独立していたのと特に名前をつける気がないので名はない。
あまり引っ張ってもしょうがないので、この日は僕がオランダに来た日。2009年4月1日に来たので今日で丸2年。
特に感慨深いということもないが、時が立つのは早いものだ。

最初の1年はいろいろわからないこともあり、不安な日々もあったと思うが、2年目はこなれたものであった。未だにオランダ語はうまく話せないし、生活は不安定ではあるが、許容範囲だろう。
こっちに来て得たものは大きい。特に昔持っていた価値観が大きく変わった気がする。主に仕事に対する考え方というやつだ。
日本にいたときは、もちろんそれが日本式なのはわかっているが、「会社が何かをしてくれるのではなく、自分が会社のために何ができるか」ということをが正義(ちょっと違うか)だった気がする。個人個人がまるで経営者にでもなったかのような考え方だと思う。仕事を通じて人間形成をするとか、会社の利益のために何ができるかとかそんなことだ。
それ自体は別に悪くないと思うし、日本という国はそうやって今の地位を気づいた部分もあると思う。でも、そこに僕(自分)はいないんだよね。
その考えがこっちに来て大きく変わった。もともとその考えに対して大きな違和感を持っていて、それが元で転職したり、カナダにワーホリにいったりしたのだが、その違和感がここに来てはっきり分かったというべきか。
こっちの人たちは、ある意味当たり前だが、日本式の考えでは働いていない。まず、自分があって会社は、その人のキャリア形成にもよるが、生活の糧を得る手段といったものだ。だから、より良い条件を提示する会社があればすぐにそちらに移る。勤続年数なんてものに意味はなく、自分が何をしたか何ができるかということにだけ意味がある。なので、会社への忠誠心とかは薄く、そもそもそんなものないと思うが、自分の生活もしくは家族が第一である。
よくあるドラマの台詞(よくあるのか?)で「仕事と私どっちが大事なの?」なんてのがあるが、この国ではそんな台詞吐くまでもなく答えは明白である。

日々の生活に特に不満はない。むしろ、もう日本に戻れないだろうという気さえしている。堕落したのかこれがあるべき姿なのかは分からないが、あまり気負って仕事しなくていいというのは心にゆとりができる。もしかしたらできすぎたのかもしれないが。
3年目は何を得るのか分からないが、楽しい1年になるという気はしている。