2012-03-31

死刑廃止論

Twitterでリツイートされていた記事を読んで感じたこと。
元記事はこれ:「自分の子どもが殺されても 同じことが言えるのか」と 書いた人に訊きたい

多少批判的なことをを書くので、その前に自分の立場を明確にしておこうと思う。僕は死刑賛成論者である。理由は「凶悪犯罪を犯したものの再犯率を考えれば死刑にした方がよい」というもの。性悪説支持とも言えるか?そもそも、日本で死刑を求刑されるほどの犯罪ってかなりの凶悪犯罪だった気がする(うろ覚え)。なので、同規模の犯罪を再犯されるよりは、そいつの命1つ奪っておいた方が社会的に利益が大きいだろうというもの。別に人は更生できないと言っているつもりはない。また、上記の記事にあったような「被害者家族のことを考えろ」というものではない。

この記事を読んでまず思ったのはちょっとコメントを恣意的に拾いすぎだろうというもの。僕は勝間和代という人がどんな人か知らない。ひょっとしたらいわゆるプロ市民、極左な人であまり世間でいい評判が無いために何を言っても批判、罵倒を浴びる人なのかもしれない。また、その動画を見ていないので本当にそんな意見ばかりが流れたのかも知らない。
最後まで記事を読んだ後に感じたのは、「著者は単に死刑賛成論者を批判したかっただけではないか」というもの。確かに言っていることは正論だし、まぁ筋も通っているなぁと思ったんだけど、問題なのは「自分がどちらの立場かを明確にしていない」ということ。ひょっとしたら冒頭の部分で反対論者であると言っているのかもしれないが、もしそうなら言い回しは非常に分かりにくく、あえてぼかしているように見える。しかも、最後に自分が当事者になったらスタンダードは変わるなんて言っている辺り、自分に降りかかる批判を避けているだけにしか見えない。
少なくともコメントを残した人は自分の主張をしているので、議論をするのであれば立場を明確にしないと話にならないのではないか?(あぁ、だから著者は反対論者で自分が当事者になったら賛成論者になると明言しているのか)

国語の成績は悪かったのでこの著者が「本当に言いたいこと」というのが読み取れていない可能性が高いが、理路整然(ともしていないが)と頭ごなしにコメントを残した人を非難しているように見えたので、なんとなく噛み付いてみた。
議論すべき問題だとは思うけど、この記事は持ってく方向がおかしい。 (そもそも議論ではないのか)

2012-03-27

署名と検証

Pure JavaScriptでRSA署名するソースを公開しちゃいますよ~~~~w
なんてのを見つけて、こりゃ手軽にSagittariusの(crypto)ライブラリ(というかRSAか)が上手く動いているか楽に検証できるや、と試してみた。

結論を言えば、上手く動いていそうなのだが、ちょっと戸惑った。(最初にX.509の証明書から公開鍵を引っ張り出す作業が必要だったが、まぁこれはいいか)
最初に試したのはデフォルトの挙動。これが戸惑った部分で、署名が毎回違う値になる。でも検証すると毎回OKがでる。上記のサイトは同一メッセージなら毎回同一の署名。
正直一瞬バグを疑ったが、よく考えてみると署名と検証で使うエンコーダとデコーダが指定できる作りになっているのを思い出した。っで、デフォルトだとPKCS #1 EMSA-PSS-ENCODEを使っていて、こいつはsalt入れて毎回違う値を吐き出すようになっている。(まぁ、その方が安全だしね)
っで、もう一つ実装してある、 PKCS v1.5 エンコーダを試してみたらまったく上記サイトと同じ値になった。どうやらよさげ。

っで、気になるのはx.509との兼ね合いを考えた際にどっちがいいんだろう?というもの。多分そのうちx.509の証明書を読み込めるようにするので、デフォルトでとりあえずx.509の方式にしておきたいなぁと。RFC読めということかな・・・

RFC2898(PKCS#5)斜め読み

斜め読みもしてないな、眺めただけか・・・

何が知りたかったかといえば、PBEが意味するところと、PKCS#5自体が暗号化のアルゴリズムを規定しないということ。実際そうだった。
では何を定義しているのかと言えば、(眺めただけなので違うかもしれないが)もう一つ上の層というか、より安全に暗号化する方法みたいなものと、とりあえずこいつに従っておけば中身は違っても表面上は何をしているかが統一できるといった感じのもの(だと思う)。
たとえば、派生鍵の作成手順とかその鍵を使った暗号化とかそんなの。でも派生鍵を作成する関数(KDF, Key Derivation Function)は外から渡す必要があるし、暗号化そのものを行う関数や、ハッシュ関数も外から渡す必要がある。 (もちろんMAC関数も)
嘘。KDFに関してはPBKDF1とPBKDF2がどういった関数であるけPKCS#5(RFC2898)内で定義されてた。MACも書いてある。さすがにハッシュ関数は別物だけど。

そうは言っても渡された関数に渡すパラメータとか統一できんよというために、関数が受け取るパラメータも定義してあって、その中にどのアルゴリズムを使うかを指定できるようになっている。まぁOIDでなわけだが。

問題にするところとしては、(crypto)ライブラリを書き直すもしくはてこ入れをするとして、このPKCS#5的に直すのか、とりあえずこういったものも実装しやすいように直すのかで大分方針が変わる気がする。後者かな。PKCS#5をサポートしたかったら(rfc pkcs :5)とか(pkcs :5)とか提供すればいいわけだし。ただ、派生鍵をどうするかは考えないと。こいつばっかりはアルゴリズムにどうしても依存するわけだし。(しないのか?調べないとなぁ)

どうでもいいけど、暗号関連の何かを読むと必ず「salt」って出てくるけど、塩って訳すと変だよなぁ。なんて訳すんだろう?

2012-03-26

(crypto)ライブラリを見直したい

願望形・・・

何が問題かと言えば現状ではサポートしていないアルゴリズムはまったく扱えないということ。追加もできない。別にそれでいいじゃんと言えばそうなんだけど、せっかく抽象化しているのにサポートしてないものはべた書きでは面白くない。
ということで、APIを見ながら何がどうなればいいかということを考えてみることにする。
cipher
アルゴリズムをラップしたCipherオブジェクトを返す。現状では与えられたCipherタイプが定義済みの共通鍵方式かどうかを判別している。(まずはこれか)
encrypt
decrypt
Cで書かれたsubr。中身はやっぱり共通鍵かどうか見てディスパッチしてる
signature
verify
Cで書かれたsubr。共通鍵ならエラーは上げる。Schemeで書かれたsignerとverifierを呼び出している。
他にもcipher-keysizeとかあるけど、とりあえずはいいだろう。後はモードとサポート済みアルゴリズムだし。generate-secret-keyとかも考える必要はあるだろうが、とりあえずいいか。
(しかし、結構でかいライブラリの割りに、口は少ないなぁ。いいことなのか?)

っで、どうしたいか。今回まずいなぁと思ったのは、PKCS#12のファイルを読み込む際にPBEが複号できないといけないのだが、(crypto)ライブラリではサポートされていない上にcipherを作ることも追加することもできない。 これはよろしくない。(元々はlibtomcryptの薄いラッパーのつもりだったし十分ではあったのだが)
とりあえず案としては、pimplみたいな感じで<cipher>と<cipher-spi>(名前はJCEから)クラスを作って、標準サポートに<builtin-cipher-spi>、ユーザーが追加したかったら<cipher-spi>を継承したクラスを作るという風にしたらいいだろうか。ちょっと考え方がJavaよりで、関数型ではないかもしれんが・・・

2012-03-24

The catcher in the rye

多分このタイトルどこかにあると思うが。

街まで出る必要があったのと、さらに時間を潰す必要があったのでポケットに入るサイズの本を探したらこれかアリスしかなかっただけだが。
時間つぶしのためスタバ(オランダにもあるんですよ)に入ってコーヒーとアップルパイを貪りながら読んでた際にある一文が目に入ってすごく共感したなぁという話。 以下がその一文。

The catcher in the rye, chapter 2.
"I'd like to put some sense in that head of yours, boy. I'm trying to help you. I'm trying to help you, if I can."
He really was, too. You could see that. But it was just that we were too much opposite side of the pole, that's all.
最後の文がそれなんだけど、最初のSpencer先生の行からじゃないと意味不明なのでそこから引用。この前の部分で先生がHolden君に対して何でこんな解答をテストでしたんだみたいなのがあって、落第していくHolden君をなんとか救おうみたいな流れ。
昔読んだときは正直それを突っぱねるHolden君に共感できなかったんだけど、今なら分かる部分がある。Spencer先生は最初に傷口に塩を塗りつけて、ほら君はこんなことをしたんだよ、なんでやったんだい?みたいに聞いていて、そりゃあ意固地にもなるわなぁと。実際自分もこれと似たようなことを何度も何度もやられたので。
ここで大人な対応でいろいろ受け入れられればいいんだろうけどそれが出来る人間ってどれくらいいるんだろう?
例えば仕事とかで何かミスを犯す度に上司から3年くらい前のことまでひっくり返されてネチネチ言われた挙句、「俺はお前を助けてやってるんだぞ」みたいなことを言われてさてどれくらい信じられるかと。
それでも仕事なら給料のためとか割り切れるけど、僕の場合はこれが人間関係だったので心が折れた。

久しぶりに読んだ本で昔は共感できなかった青臭い部分に共感している辺り、精神年齢が下がっているのだろう。もしくは大人になれてないか。

2012-03-23

マルチスレッドテスト(結果)

連投だなぁ、まぁいいか。
多少の制約があるものの、まぁ元気に動いている感じ。っで、せっかくマルチに走らせているのだから、どれくらいパフォーマンスに影響が出たかというところが気になる。っで調べてみた。

実行環境: Cygwin on Windows 7 (64bits), Core i7
シングルスレッド
% time sash run-test.scm sitelib
;; ログ省略
sash run-test.scm sitelib  3.99s user 0.25s system 99% cpu 4.243 total
マルチスレッド
% time sash run-test.scm sitelib
;; ログ省略
sash run-test.scm sitelib  4.79s user 0.25s system 252% cpu 1.997 total
※ 実行スクリプト内を直接いじっているので、コマンドは一緒に見えるけど、中身は違う。

2倍速!CPUの使用量が200%を超えとる。やった甲斐ありと思っていいかな。

マルチスレッドテスト(依存関係問題)

昨日の変更で一度作成されたライブラリはグローバルに持つようにしたので、キャッシュを読み込むときに問題が起きるようになった。
問題としては(恐らくだが)、スレッドAがライブラリhogeをキャッシュから読み込んでいるときにスレッドBも同じライブラリを読み込むとどうなるかというもの。 現状だとキャッシュの読み込みには特にロックを取得していないので、データ読込→ライブラリ作成→データ復元→インポートという流れの中で問題が起きるのかなぁと(確証ない、状況証拠のみ)。

上記の仮定が正しいとして、解決方法を少し考えてみた。
  1. キャッシュ読み込み時に、プロセスリストを作成し、そのリストの中に自分が必要としているキャッシュがあれば待つ。
    でもこの場合同じライブラリだけど別ファイルだったらどうなるんだ?
  2. キャッシュ読み込み自体にロックをかけて、ライブラリが存在しているか確認、あれば帰る
1の場合は同一ライブラリ別ファイルの対処が多分不可能。2の場合はせっかくマルチに走らせるのにキャッシュの読み込みがボトルネックになる。実装としては2の方が楽。
マルチスレッドでloadを使うなんて事がそうあるとは思えないので、とりあえず2を実装してみればいいだろうか?

2012-03-22

マルチスレッドテスト(調査)

テストケースをファイルをロードしてテストするという風に変更したのだが、スレッド作って可能な限り同時に流した方が効率がいいよなぁと思い簡単なテストをしたらこけた。
っで調査。

問題になるのは、共有オブジェクト(DLL、so)で作られたライブラリを読み込むテスト。(意外な依存関係があったりするので、ピュアスキームだけマルチでって分けにはいかないのが辛い)。
なぜかその類のライブラリがあるとインポートエラーになる。ちょっと実装を覗いた。共有オブジェクトは一度呼ばれるとその情報が保存されて、同じオブジェクトを2度読み込まないようになっている。まぁこれはいい。っで、その中に初期化関数があるのだが、読み込みの際にそいつも一緒に呼ばれる。そうするとオブジェクトは初期化されたというフラグが立つ。ここに問題があった。

親スレッドが呼ぶ分にはまったく問題ないんだけど、複数の子スレッドで同一のDLLが呼ばれると問題になる。
この辺は多少以上に設計方針に関わっていて、子スレッドでインポートされたライブラリは親スレッドに影響を与えないという作りにしてあるのだが、これが拙い。兄弟間ではライブラリの共有が出来ないからだ。そうすると一度呼ばれたライブラリは2度と初期かされないので、兄が呼ぶと、弟が呼んでも答えてくれない。
解決策はまぁ、2つだろう。子供は親に迷惑をかけるものだと割り切るか、ターゲットを八方美人にするか。設計方針的には後者になるか。ただ、実際2度初期化をするとおかしくなるものもあるので、そこは気をつけないとまずいか。

2012-03-21

Weak hashtable

マクロ周りの修正と、デバッグ情報(主にファイル周りの情報)を何とかしようとしたらメモリがあっという間に飛ぶようになったので、ちょっとまじめに実装しようとしてみた。

問題になったのは、VM側にソースの情報を持たせているのだが、こいつが普通のハッシュテーブルなので要素は増えるけど減らないというのがある。たとえばユニットテストなんか走らせると最低でも500MBぐらい必要だった。

元もと使ってはいなかったがWeakハッシュテーブル自体はコードにあって(Gaucheからのパクリ)、単にこれを使えばいいかなぁと思っていたのだがそう単純な話でもなかった。

何が問題だったか。
GaucheのWeakハッシュテーブルはキー、値、もしくは両方という感じでどの要素がWeakポインタなのか指定できる。しかし、そのままの状態でうまく動いたのは値を指定した場合のみで、キーまたは両方を指定すると値が取り出せないか最悪クラッシュする。(最新版のソースは知らない+Gauche上で使ったことないのでその辺は考慮されているのかもしれない)

どうしてそれが起きたのか。
Weakハッシュテーブルに要素を入れるとき、GCが辿らないように要素を一段ラップするようになっている。問題はここで、値だけなら問題ないが、キーをラップしたときラップされたポインタは闇に消えていた。なので、値を取り出そうとすると必ず失敗する。(だって、キー自体とラッパーは違うんだもん)

ではどうしたか。
非常に醜い方法でとりあえず回避。ハッシュテーブル自体にラップされたキーの情報を持たせるようにした。値を入れる際にキーの情報も一緒に保存しておく。っで、取り出す際には保存先から生のキーを元にラップされたキーを取り出す。ただ、これ自体がGCから辿れると結局回収されないので、これ自体もWeakベクタで実装。ただ、キーの情報が増えるとベクタが爆発的に増えるのでパフォーマンスに影響がでる。(というか出てる)。

最終的にはメモリ使用量が最大で200MBくらいに落ち着いたので、まぁよしとしている(これ以外にもいろいろやっているが)。キャッシュされてしまえばソース情報は消えるわけで、コンパイルもスキップされるから速度的には問題なくなると思うし。

2012-03-19

なんとなく

自分のブログの過去ログを眺めていた。2006年からこのブログ書いてるんだとちょっと感心。
最初の頃はPerl、C++にくびったけだったのが分かる。あと文章が幼いw(今も幼いが・・・)
FTPの実装方法が分からないなんていってる自分がいるのを見ると、技術的なスキルや知識はついたんだなぁと思う。(まぁ、Socket周りなんて仕事ではなくSagittariusの開発からだけど)
2009年からはこっちにいるのだが、その頃辺りからSICPとかSchemeの話題が出てくる。でもその前から言語の処理系を作りたいみたいな文章があって、その辺は変わってなんだなぁと実感。

結構いろんな箇所で自分は感情が欠落しているというのを誇張しているが、まぁ軽い厨二病をこじらせていたのだろう。まぁ、今では立派な甲斐性なしにレベルアップしたけど。

短い文が多いのは当時はブログに何を書いてもいいだろうと思っていたんだろう、どうせ人は見ないしと考えて。(今でも需要があるとは思わないが)。

読んでもその当時自分が何をしていたのかってのがいまいち思い出せない。けど、いろんな意味で自分は変わったんだなぁと実感した。本質は変わってないんだけど、枝葉というか、まだ若くて何も知らなかった自分(25、6なのに)というのから、人の目を気にして、評価を気にして、大切なものが何なのか分からなくなった自分という感じではあるが。(じゃあ、僕の本質って?多分あほなんでしょう。または考えなしか)

自分がしたことに後悔がないと言えば嘘になるし、可能であれば取り返したいという気持ちもあるけど、後ろを見てもしょうがないよねというのが一番強いではある。覆水盆に返らず、僕だけが水を溢したわけではないと思うけど、一度失ったものは帰ってこない。この国で独りで生きるのはまぁ辛いだろうが、なんとかなるだろう。 退かぬ、媚びぬ、省みぬ!(だっけ?)

promiseとfuture

C++11から入った機能。JavaのFutureとよく似てる?

まぁ、C++で使うことはないのでSagittariusですごくナイーブに実装してみた。ひょっとしたら使うようになるかもしれないというだけで。
(import (rnrs) (srfi :18) (clos user))

(define-class <promise> ()
  ((future :init-keyword :future)
   (thread :init-keyword :thread :accessor promise-thread)))

(define-class <future> ()
  ((promise :accessor future-promise)))

(define (make-promise proc . args)
  (let* ((thunk (lambda () (apply proc args)))
  (thread (make-thread thunk))
  (future (make <future> :thunk thunk))
  (promise (make <promise> :future future :thread thread)))
    (future-promise future promise)
    (thread-start! thread)
    promise))

(define-method get-future ((p <promise>))
  (slot-ref p 'future))

(define-method get ((f <future>))
  (let ((promise (future-promise f)))
    (thread-join! (promise-thread promise))))

(let* ((promise (make-promise (lambda args
    (thread-sleep! 1000) ;; heavy routine
    (apply + args)) 1 2 3 4))
       (future (get-future promise)))
  ;; do something 
  (print 'here)
  ;; wait
  (print "get value " (get future)))
こんな感じ?あんまり何も考えてない。値の取得が2回起きたらどうなるとか。なんかこの程度だとスレッドの薄いラッパーにしか見えないなぁ。どうするのが正しいんだ?

2012-03-18

WATCOMとGCCの違いメモ

結構違いがあってびびった。まだコンパイルが通るだけでまだまともに動かないが・・・
あと、WATCOMのターゲットはWindowsなので、UNIX関連はまた別になるけど
voidの関数が値を返すようなreturnをするとコンパイルエラー
これはGCCやMSVCが甘すぎるのだと思う。正直こうあってほしい。
small、medium、large、hugeがマクロとして定義されてる
Linux版GCCのgetcとかputcのマクロ並に邪悪です。速攻undef。
構造体の初期化で使えるのは定数のみ
結構使っている箇所があった。正直許せよこれくらいと思った。
NaNが0より大きい
これははまった。数字じゃないのに、そんなの返すなよ。多分コンパイラのバグ(と思う)。
左シフトで桁あふれすると0になる
Cの規格では未定義動作なのでコンパイラの警告を無視していた僕が悪いのだが。
静的領域や大域変数は4バイト境界に配置
8バイトを期待したコードを書いていると困る。doubleを噛ませても問答無用だった。__farキーワードで回避
exp、log、powが微妙
まだ原因を特定してないけど、上記の数学関数がOverflowやらRange errorやらDomain errorを投げる。GCCとMSVCは投げない。多分シフトの桁あふれと関連してるのだろう。中身があんまり賢くないようだ。
externされている大域変数の参照が不正
__farキーワードのせいなのか、LoadLibraryの問題なのかは分からない。後者はWin32 APIだからちょっと考え難いか。前者かな?リンカーという線もあるが。
modfにINFINITYを渡すとNaNを返す
GCCとMSVCは0を返す。
根本的にNaNの扱いがおかしい
NAN==0.0が真になるとかありえないだろう。他にもいろいろ偽を返すべきところで真を返す。
logに-INFINITYを渡すとDomain error
だけならいいんだけど、そのまま-inf.0を返しやがる。+nan.0を期待しているのだが・・・
エクスポートされる関数の名前がおかしい
普通は_nameなんだけど、name_と逆になる。dllexportがおかしい?。__cdeclをつける必要あり。
最後のが特定できてないのでR6RSのテストケースが通らない。通した。modfのNaN問題だった。
また、動的モジュールのロードがおかしいのか分からないけど、拡張モジュールがまったくテストできない。
あまりメジャーな処理系ではないらしく、資料もそんなにないし、マニュアルでは痒いところに手が届かない(8バイト境界とか)。2日で何とかなるとは思っていなかったが、想像以上に癖がありすぎて挫折しそう。
とりあえずブランチ切ってコミット。他の処理系で問題なく動くならマージしようかな。とりあえず入れておけば誰か(一人プロジェクトなのに?)が突っ込みいれてくれるかもしれない。

2012-03-17

Watcomにはまる

自宅のThinkPad X60+Cygwinの環境にちょっと疲れてきたので(遅い、不安定等)、Windows上で使える第3の選択肢としてWatcomでビルドできないか試行錯誤中。っで、ちょっとした問題が発生した。
VC、GCCともにメモリのアライメントが8ビット(バイト?)境界なんだけど、Watcomは4ビットだった。これ実は大問題で、いろいろ8ビット境界を想定して書いているので思わぬところでこける。
オプションで指定できるのは構造体のアライメントだけっぽいし、staticな値を8ビット境界に置くにはどうしたらいいんだろう?ダメなら諦めるしかないが・・・

2012-03-16

クロージャー?

Twitterで呟いたが、Javaの無名クラスは厳密にはクロージャーになりえず、JDK8辺りで入る予定のlambdaも無名クラスの糖衣構文になるとかという話を昨日バーで同僚としていた。
糖衣構文でもあれば便利だろうなぁと思う場面が山ほどあるので、是非早いとこ実装してもらいたいが(JDK7では見送られた)。
話に参加していたもう一人の同僚が、違いが分からないと言っていたので、関数型言語のクロージャーとどう違うのかというのでも書いてみようかなぁと。ただ、僕自身あんまり理解してないので間違いは山ほどあるかと・・・

そもそも、クロージャーとは何ぞやと。詳しい説明はWikipediaでも見てもらうとして、上記で議論になっていたのは捕捉時の環境が閉じているかどうかということであった。JavaやC++ではここが完全に閉じることができず、渡された自由変数が参照渡しになる。以下はJavaでそれっぽく見せるようにしたコード。(C++では補足さえ出来ない)
package closure;

public interface Lambda<T> {

 T apply(Object... args);
}

// 別ファイル
package closure;

public class Closure {

 private Object obj;
 
 public void run() {
  // property
  Lambda<Object> lam = new Lambda<Object>() {
   @Override
   public Object apply(Object... args) {
    obj = args[0];
    System.out.println(obj);
    return obj;
   }
  };
  final Object obj2 = (Object)"string";
  Lambda<Object> lam2 = new Lambda<Object>() {
   @Override
   public Object apply(Object... args) {
    // obj2 = args[0]; // *1 compile error
    System.out.println(obj2);
    return null;
   }
  };
  System.out.println(lam.apply("here we go"));
  System.out.println(obj);
  lam2.apply();
 }
 
 public static void main(String[] args) {
  Closure cl = new Closure();
  cl.run();
  
 } 
}
*1の部分が重要で、Javaでは無名クラスの中から参照できる自由変数(とここでは呼ぶ)は変更不可能でなければならない。多分JVMの実装上も問題だろう。lam2がrunから戻った際にスタックからobj2が消えるのでその辺が問題になると思われる。
ではわれらがSchemeではどうだろうか?
(define obj #f)

(define (runner lam1 lam2)
  (print (lam1 "here we go"))
  (print (lam2 "we can do it")))

(define (main args)
  (let* ((obj2 "string")
  (lam1 (lambda (o) (set! obj o) o))
  (lam2 (lambda (o) (set! obj2 o) o)))
    (runner lam1 lam2)
    (print obj #\: obj2)))
#|
here we go
we can do it
here we go:we can do it
|#
あまりいい例ではない気もするが、lam2で捕捉されたobj2の値が変更されるとlambdaの外側のでも問題なく変更されている。
これが出来て何が嬉しいかと言われると正直微妙ではある。(結構使うけどこういうの)
ただ、Javaでもfinalつければ捕捉出来るんだし、ほぼ同じじゃないのだろうか?(いい加減)
(define obj #f)

(define (runner o run?)
  (let* ((obj2 o)
  (lam1 (lambda (o) (set! obj o) o))
  (lam2 (lambda (o) (set! obj2 o) o)))
    (cond (run?
    (print (lam1 "here we go"))
    (print (lam2 "we can do it"))
    (print obj #\: obj2))
   (else
    (values lam1 lam2 obj2)))))

(define (main args)
  (let ((o "string"))
    (runner o #t)
    (receive (lam1 lam2 obj2) (runner o #f)
      (print (lam1 "here we go"))
      (print (lam2 "we can do it"))
      (print obj #\: obj2))
    (print o)))
#|
here we go
we can do it
here we go:we can do it
here we go
we can do it
here we go:string
string
|#
こっちの方が捕捉時の環境というのが捕らえやすいだろうか?あんまり変わらないか?

2012-03-13

British or American English?

カナダにいたときに購読し始めたニュースレター(今はほとんど読んでなかった)から届いた面白げなタイトル。
その中にあった動画の一つがこれ
簡単な英語と米語の語彙の違いを紹介している。違いは以下のような感じ
BritishAmerican
TrousersPants
PantsUnderwear
視ると面白い。MeanがずっとBritishの方しか知らなかったり(Being Nastyなんて知らなんだ)。意外と米語だと思っていたのが実は英語だったとか(GardenとYardとか。スコットランドヤードって言うやん!)
自分の語彙を見ると主に日本で習う英語は米語だと思うが、たまに変なのもある(Trousersとか、学校ではこれがズボンと習ったなぁ。でも下着はUnderwearだった気がする)
謳い文句には文法も書いてあったのにサイトにそれらしいものは特に無かった。残念。

昨日の続き

意外とてこずっている。(まぁ、当たり前か・・・)
syntax-caseの実装が甘いのがここにきて痛い感じだ。
たとえばこんなコード。
(define-syntax print
  (syntax-rules ()
    ((_ o)
     (begin (display o) (newline)))))
(print
  (let ((x 'outer))
    (let-syntax ((m (syntax-rules () ((m) x))))
      (let ((x 'inner)) (m)))))
xという変数は合計で3回出現しているが、printマクロを噛ませることですべて同じ識別子へと見事な変貌を遂げている。これが問題なのだ・・・
syntax-rules内のxが3回目のxと同じため出力としてはinnerが帰ってくる。でも、printマクロを噛ませないとouterが帰る。これは最初のxが束縛された際の環境をマクロ展開器が知っているため、単なるシンボルであれば環境を閉じ込めて識別子にすることができるからだ。

あぁ、そうか。ならラップする際に何とかできればいいのか。いや、待てよ。同じ識別子なんだから、環境内でも同じか。結局簡単な方法はunrenameすることになるのか・・・

さて、もう一ひねりしてみるか・・・orz

2012-03-12

マクロとコンパイラと不具合と

ずいぶん昔から以下のようなマクロが動かない。
(define-syntax define-lambda
  (syntax-rules ()
    ((_ name formals body ...)
     (define name (lambda formals body ...)))))
(define-lambda test (t rest) `(t ,t))
(test 'a 'b) ;; => (t.xxxxxx a) xxxxxxは適当な16進数
なぜか?実は原因も全部分かっているのだが、直すに当たって余計な不具合を入れないためにもこれが必要だった経緯を書いておく。

そもそも、これは何か?
これはsyntax-caseのR6RSテストスイートをパスさせるために入れた苦肉の策である。
具体的には、syntax-caseを通った式はすべて識別子に変換される。その際に、識別子の同一性を保つため同一のsymbolであれば同一の識別子になるようにしている。

何故これは起きるのか?
上記のままでは拙い場合があった。問題としては一度識別子に変換されると2度目の変換ルーチン(以下renameと呼ぶ)ではそのままその識別子返してくる。その際に、ローカルに束縛された識別子が問題になった(と思う)。だが、マクロの展開器は賢くないのでコンパイラ側でローカルに束縛された識別子をシンボルに直してやる必要があった。また、その際に単に構文情報を剥ぎ取るだけではだめで、シンボルに直された識別子が一意である必要があった。
上記の例では(define-lambda ...)の中で「t」というシンボルはすべて同一の識別子に変換される。普通にquoteされるなら構文情報を剥ぎ取るだけなので問題ないが、quasiquoteまではunrename(と呼んでいる)ルーチンは見ない。そのため、quasiquote内の最初の「t」はunrenameされた状態で出力される。

どう解決するか?
解決方法は2つあると考えていて、unrenameがquasiquoteを見る。もしくは、unrenameをやめる。
前者は正直これ以上糞コードを増やしたくないので却下。後者はなぜunrenameなどと言うものが必要だったかという経緯を考えれば問題ない気がする。
つまり、renameルーチンが識別子を受け取った際に、その識別子をコピーしてやればいいはず。そうすれば、識別子が同一過ぎて困るという問題は無いはずだ。具体的にはwrap-syntaxという処理が式を識別子に変換しているのでそれをちょっといじって、コンパイラからunrename関係の処理をごっそり削ればいけるはず。

とりあえず試してみる。

2012-03-08

開発環境

ほぼ月1でリリースしてると、月1でフルビルドしないといけないということになっていて、Cygwin+非力なマシンでは厳しいなぁと思えてきた。
もっと非力なマシン+Linux GCCだと速いという事はやはりCygwinが遅いんだろうなぁと思い、何か別な環境はないかと探してみる。

とりあえず基準というか絶対条件として、
  • CMakeがサポートしてるコンパイラ
というのがどうしても入ってくる。そうするとどうしても問答無用で絞られてきて、現状の最新バージョン2.8.7ではGCC、MinGW、MSVC(含むVSプロジェクト)、Borland C++とWatcomであった。まぁ、GCCの部分はUnix Makefileなのでコンパイラ自体はなんでもいいのだが。結局Cygwinを避けるためには自前でmake相当のコマンドを持っているコンパイラになるわけだ。
っで、BCC32(フリー版の5.5)は僕が大学の時からバージョンアップしてないという記憶なのでさすがに10年以上前ではどうよ?と思いパス。(まぁ、こじゃれた最新機能はつかっていないのでOKな気もするが)となるとWatcomしかないじゃんと思い試してみた。

結論。BoehmGCすらコンパイルできなかった・・・orz

多分どこかCMakeLists.txtの記述がおかしいのだろうと思うのだが、Windows環境ではMSVC決め打ちの部分が多いので修正も大変。とりあえずwmakeが打てるところまできてこれが起きたので一気に気力が持っていかれた。

じゃあ素直にMSVCでいいじゃんと思うのだが、インストールに2G以上使うような富豪プログラムなのでHDDのスペースが足りない。(Cドライブが10Gしか切ってない上に、すでに8G使われている)
Windows SDKは問答無用で.NETがくっついてくるし、打つ手なし。

だれかいい案ないですか?

2012-03-07

DER, BER, CER?

ASN.1の代表的なエンコード(?)方式。
正直CERは見たことないのでよく知らないが、職業柄DERはよく見るしそれにくっついてBERもよく見る。

Sagittariusに(ようやく)ASN.1のバイナリを読むライブラリを入れたのでちょっといろいろ整理も兼ねて。
基本的な部分はBouncycastleの移植といった感じにしてあるので、クラスの継承関係もそんな感じ。
DERを読むならBERを読むのも簡単なので、DERを基本クラスにしてそのBERは派生。
Readerはタグみて読むだけなので、クラスの派生を知る必要はないが、両方読めるようにタグの種類でdispatchする感じ。
エンコードはおそらく本来はBERのオブジェクトも条件によってはDERを出力しないとまずいんだろうけど、面倒なのでBERだけ出力。つまり、BERを含む証明書(あるのか?)は読み込みしてDERだけに変換という荒業はできない。(これはそのうち何とかするかも。parameter使えば多分なんとかなる)
昔作ったASN.1のパーサーがあるけど、これどうしよう?一応バイナリと構文の整合性チェックに使う予定だったんだけど、やるのしんどい・・・

DERとBERの違いはなんぞやというGoogle検索の結果を勝手に予測して。
ここが詳しいけど、一応自分でも。
本質的には一緒。DERはタグ、長さ、データの順で書かれたバイナリ。タグは基本2種類あって、プリミティブと拡張(?訳語しらない)されたもの。最初の1バイトと0x1Fを論理積(and)した値が0x1Fなら続く数バイトはタグになる。
BERの方が機能的にはスーパーセットらしい(構築されたoctet stringとか?)。PKCS12形式の証明書群の中にあるのを見た。多分暗号化されたDERとか入れれるのだろう。(他のViewerで見るとどうもSagittariusのはうまく読めてないくさい。そのうち直そう)

2012-03-02

Sagittarius version 0.3.0リリース

リリースノートを書くのが億劫というか、何を直して何を追加したか思い出せん。もっとまめにChangeLog書かないと・・・

Sagittarius Scheme 0.3.0がリリースされました。このバージョンからCLOSが組み込みでサポートされます。ただし、すべての機能がサポートされているわけではありません。使用するには(clos user)もしくは(clos core)ライブラリをインポートしてください。

修正された不具合
  • internal defineされた値がマクロ内から参照できない問題が修正されました。
新たに追加されたライブラリ
  • (clos user) 及び (clos core)が追加されました。CLOS風のプログラムを書くにはこのライブラリをインポートする必要があります。
  • (text markdown)が追加されました。簡単なマークダウン形式のテキストのパース及びHTMLへの出力を行います。
  • (scribble)が(scribble parser)、(scribble convert)及び(scribble plugin)に分割されました。
  • (sagittarius document html)が追加されました。Sagittariusが作成するHTML形式のドキュメントを作成するのに使われています。
  • (getopt)が追加されました。SRFI-37の薄いラッパーです。
改善された機能
  • regex-replace-all 及び regex-replace-firstの第2引数がプロシージャを取れるようになりました。Javascriptの機能にinspireされたものです。詳しくはドキュメントを参照してください。
  • binary-port?及びtextual-port?がR7RS互換になり、ポート以外の引数を受け取ってもエラーを投げなくなりました。
  • get-bytevector-n及びget-bytevector-someのパフォーマンスが多少改善されました。内部的に2度メモリの割付を行っていたのが解消されています。
  • (sagittarius process)の機能が大幅に改善されました。JavaのProcessクラスの設計思想に基づいて書き換えられています。
  • (sagittarius threads)でしばしばデッドロックを起こす問題が解消されました。これによりドキュメントのサポートSRFIの欄に正式にSRFI-18が追加されています。ただしWindows上でthread-terminate!を使うと予期しない動作をします。現状確認されている不具合です。
新たに追加されたドキュメント
  • (sagittarius process)が正式にドキュメント化されました。

多分これ以上にいろいろいじったんだけど、思い出せない。

rebase問題+process

Sagittariusでプロセスの実装をしている際に、Cygwin上だとforkでころころ落ちてていらいらしていた。Cygwinのforkは不安定だという話は聞いていたので、こんなものだろうかと思っていたのだが、(実際、出てくるエラーメッセージもresource temporary not availableだったし)、いろいろ調べてみるとrebaseすると直るみたいな記事が山ほどあったので試してみた。

結果、あぁ、これだったのね、という感じで今はさくさく動いている。別段Cygwin上でプロセスを起動させることはないのだが、(これ自体Windowsのバッチでmavenのビルドスクリプトを書くのが苦痛で作ったものなので^^;)、動くとなるとなんらかに使えるかなぁとか考えてしまう。

と、これだけだと微妙なので、新生プロセスライブラリの概要でも。(誰も見てない?)
低レベルAPIのシグネチャ(これって日本語でなんていうの?)は特に変更なし。ラッパーで作っていたrunプロシージャが(name args)から(name . args)になって、書くのが多少楽になった。
今までは問答無用で標準入出力にリダイレクトしていたけど、パイプを通してSchemeからそれらの出力結果を取得できるようにした。(低レベルAPI)

実装上どうしようもなかったのだが、POSIX環境とWindows環境で微妙な差がある。POSIXだとプロセスの使い回しができるのに対し(PIDは一緒だが)、Windowsだとできない。これは、Scheme上では見えないけど、プロセスオブジェクトを作った際に実際にWindowsではプロセスを作ってしまうが、POSIXは後からforkとexecで実行するための情報を格納しているだけという違い。
設計思想的には両方ともWindows風にしてしまいたいが、僕の頭では無理。

理由:
プロセスオブジェクトを作成した段階でそれぞれのリダイレクト先にアクセスできるようにしたかった。でないと走らせる前に入出力のリダイレクト先を読み取るとか書き込むということができないから。また、作成と同時にプロセスが走るようにするとオブジェクトを作った意味が薄れるため。
誰かいい解決方法教えて。

2012-03-01

Win32プロセス

プロセスの実装を何とかしようとしているのだが、調べても答えが出てこない問題がでた。

現状、プロセスを作成した際に標準入力、出力、エラーを名前なしパイプに割り当てている。っで、プロセスは非同期もしくは同期(単に終了まで待つだけ)で起動できるのだが、MSDNを読むと作成されたパイプはバッファーの上限があるらしく、上限に達した場合読み取ってやらないと書き込みができなくなるという。
あんまりログを吐かないプロセスなら問題ないが、mavenでがりがりログを吐く上にコンパイルされるプロジェクトが20以上あるとなるとバッファーの上限はあっという間に超える。
じゃあ、読み取ってやればいいじゃんという話になるのだが、パイプから何かを読み取ろうとした際にまだ何も書き込まれていない状態だと現状EOFを返すのでループで回してもすぐ終了してしまう。っで多分デッドロックというかバッファーがいい感じに満杯になってうんともすんとも言わなくなる。
こういう場合って多くの処理系はどうやって対応しているのだろうか?
とりあえず、読み込みの方をいじった方がいいだろうか?