Common LispとSchemeの多値の違い
Common Lispでは多値を必要としない場面で多値(正確には2個以上の値)が返された場合、
最初の値が使用され、後は捨てられます。
(list 1 (values 2 3)) => (1 2)
ここまでは知っていたんですが、同様に多値を必要としない場面で0個の値が返された場合、
nilが使用されることが仕様で決まっていることを知りました。
(list 1 (values 2 3) (values)) => (1 2 nil)
なんでもしっかり決まっているのがCommon Lispのいいところですね。
一方、Schemeはどうなっているかというと、
Schemeのvaluesは次のように定義できます。
(define (values . things) (call-with-current-continuation (lambda (cont) (apply cont things))))
つまり、現在の継続に全ての引数を渡すという定義です。
これが正しく動作するには、現在の継続が複数の値を必要としているか(例えばcall-with-values)、
もしくは現在の継続が値を捨てる必要があります(例えばbegin)。
例えば、次のような式を考えて見ます。
(list 1 (values))
R5RSの形式的意味論では、これは「引数の数が異なる」というエラーになります。
R6RSの形式的意味論では、簡約が途中でとまってしまいます。
で、実際のScheme処理系で試したらどうなるか見てみました。
まず、DrScheme
(list 1 (values 2 3)) context expected 1 value, received 2 values: 2 3 > (list 1 (values)) context expected 1 value, received 0 values
R5RSの形式的意味論に忠実な感じです。
次にGauche。
gosh> (list 1 (values 2 3)) (1 2) gosh> (list 1 (values)) (1 #<undef>) gosh> (list 1 (values 2 3) (values)) (1 2 2) gosh> (list 1 (values) (values 2 3) (values) 4 (values) (values 5 6) (values)) (1 #<undef> 2 2 4 2 5 5)
1つ目と2つ目を見た段階では、Common Lispとほぼ同じ仕様かと思いきや、
3つ目と4つ目がなんだか凄いことになってます。
これはバグ……というか仕様でしょうか。
「多値の使い方はプログラマが責任持ってね」という仕様です。
(R5RS的にはOK)