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つ目がなんだか凄いことになってます。
これはバグ……というか仕様でしょうか。

One Response to “Common LispとSchemeの多値の違い”

  1. shiro より:

    「多値の使い方はプログラマが責任持ってね」という仕様です。
    (R5RS的にはOK)

Leave a Reply