#gosh> (port-name (standard-input-port))
"(stdin)"
gosh> (port-name (standard-error-port))
"(stderr)"
gosh> (port-name (standard-output-port))
"(stdout)"
#わたしの感じていた違和感はこのことなんです。
#あーなるほど。これは一貫性がありませんね。直します。
#gosh> ((^f (if #f (f) (f 1))) (^x x))
*** ERROR: Compile Error: wrong number of arguments: #f requires 1, but got 0
#これはバグでしょうか…
#あーこれははまった覚えが。この特定のケースについていえばfの引数がおかしいのは明らかなんでエラー通知でもいいと思うんですが、ifの条件が成立しないことがインライン展開とconstant propagation後にならないとわからないことがあるんですよね。ローカル関数のη-reductionはその前の段階で引数が違うと弾いちゃう。
#η-reductionで引数の個数が違ってたらerrorを呼ぶコードに展開しとけば回避できるんだけど、それだと明らかな間違いもコンパイル時に検出できないことになる。結構引数個数チェックって役に立ってるので、どうしようか思案中です。
#ちょっとわかりにくいか。η-reductionで引数の個数が違うとわかった時に、コンパイラがエラーを報告するのではなく、そこに「実行時にエラーを出す」コードを埋め込む、ということ。そんでもって全部展開が終わってdead code eliminationした後にもう一度スキャンして、引数違いエラーのコードがまだ残っていたらコンパイラがエラー通知することにすればいいかなあ。
##f のところでarityをチェックして分岐するようなコードを書いても,呼び出し側が1ヶ所しかないと(?)事前に撥ねられてしまいます.
#gosh> (let1 g (^f (if (= (arity f) 0) (f) (f 1))) (g (^x x)) (g (^x x)))
1
gosh> (let1 g (^f (if (= (arity f) 0) (f) (f 1))) (g (^x x)))
*** ERROR: Compile Error: wrong number of arguments: #f requires 1, but got 0
#gosh> (let1 g (^f (if #f (f) (f 1))) (g (^x x)))
1
#繋げるつもりがミスって送信してしまった.引数個数チェック自体がなくなってしまうのは嫌ですね…
#うむ、arityは普通の関数なので、今のところコンパイラはfが確定していても(arity f)の値を推論できないので、(f)の部分が絶対に呼ばれないかどうかがわからないのですね。 gの呼び出しが2回あるとgをインライン展開しないのでfの展開もされず通る。また最後の例では先にifのデッドコードが削られてて通る。このへん、コンパイラの改良によっても振る舞いが変わりそうなんで、難しいですね。
#ローカルに「インライン展開しない」もしくは「引数チェックしない」という宣言を足せるようにするという手もあるが、コードがとってもCommon Lispっぽくなってしまいそうだ。
#RT : shiro: うむ、arityは普通の関数なので、今のところコンパイラはfが確定していても(arity f)の値を推論できないので、(f)の部分が絶対に呼ばれないかどうかがわからないのですね。 gの呼び出しが2回あるとgをインライン展 ...
#すみません、面白そうな話題だったので公式リツートしたらこっちに反映してしまいました。
#私は気にしてないですがびっくりする人もいるみたいなんで、公式RTは拾わないようにした方がいいかなあ。
#あまり致命的なことじゃなさそうで安心しました。ところで、"(let1 g (^f (if (= (arity f) 0) (f) (f 1))) (g (^x x)))"の例は、arityが再定義される可能性を考えると、(f)を呼ばれるものとしてエラーチェックした方が良いような気がします。
#そうなんです。inlinableである(ということは再定義されないと仮定できる)でない限り、最適化できないんですね。
#そのコードは,arityが0または1であるfを受け取ることを意図しているので,事前に撥ねられたくないのです.呼び出し側をまだ1ヶ所しか書いていない状態だと実行できなくて…
#正当であり得るコードをコンパイラがはねてしまうのはバグと言ってよいと思います。とりあえずの回避法として、どこか影響なさそうなところでfかgにset!してみてください。set!されたローカル変数については最適化が抑制されるのでもしかするとうまくいくかも。
#今のコンパイラだと、applyを使っても回避できるんじゃないでしょうか。
#gosh> (let1 g (^f (if (= (arity f) 0) (apply f '()) (apply f '(1)))) (g (^x x)))
1
#なるほど.f, gどちらでもset!入れたら動いてくれました.applyもいけました.ありがとうございます.
#とはいえ綺麗ではないので、いずれ何とかしないとなあ。