nptclのブログ

Common Lisp処理系nptの開発メモです。https://github.com/nptcl/npt

型valuesの使い方

実装していると難しくてイヤになるのが型(typeのこと)です。
型関係の実装で一番難しいのはsubtypepで、ひどいもんです。

今回は難しくはないものの簡単でもない、型valuesについて説明します。
命令valuesではなく、型valuesの方ですので注意。

型とは、普通だったらtypepsubtypepなんかで使うものです。
例えば、オブジェクトが整数かどうかを確認するには次のようにします。

* (typep 100 'integer)
T

* (typep :hello 'integer)
NIL

上記で記載した'integerというのが「型」です。
今回話題とするのは、型valuesだけです。
この型は他の型とはかなり違っており、そもそもtypepでは使えません。

* (typep 100 'values)
 →エラー

* (typep 100 '(values integer))
 →エラー

valuesが使えるのは、次の2点限定となります。

  • functionの第2引数
  • special operatorのthe

valuesというのは、値の返却値を指定する型なので、 そんなものを指定したい場合が上記の2点しか存在しないのです。

その上記2点だけという制約により、(not (values ...))(and (values ...) ...)という書き方が許されないことになります。 実装するうえでnot, and, orが許容されないのは本当にありがたいことで、 この辺りの苦労はそのうちsubtypepと一緒に話したいと思っています。 たぶんsubtypepの実装ってどこにも情報がないと思いますので。

それでは型valuesの例を示します。

(declaim (ftype (function * (values integer)) aaa))
  →関数aaaは、第一返却値がinteger

(the (values integer string) (call 10 20 30))
  →call関数は第一返却値がintegerで第二返却値がstring

values&optional&restも使えます。
例えば次の通り。

(values integer &optional string fixnum)
(values t &rest integer)

valuesにはとても分かりづらいことが1つあります。 それはデフォルトでは&rest tが指定されるということです。

(values integer)と記載した場合、 (values integer &rest t)と同じ意味になります。

さらに型functionでもtheでも、valuesと記載しなかった場合は、 自動的に(values xxx)という意味になります。 つまり、次の3つの表記はすべて同一となります。

  • (the integer ...)
  • (the (values integer) ...)
  • (the (values integer &rest t) ...)

それでは&rest tとはどういう意味でしょうか。 &rest tはそれ以降の型が全て型tであるという意味になります。
つまり、

(values integer)

(values integer t t t t t t .....)

という意味になるのです。

もし、ある関数の返却値がintegerたった1つであるとわかっている場合、

(values integer)

と記載するのではなく、

(values integer &rest nil)

と記載するのが正しいことになります。

よくマクロに関係する関数なんかでは

(defun aaa ()
  (values))

みたいに、返却値が全くないことを指定することがあります。 上記の関数の命令(values)は正しいのですが、 もし返却値の型をvaluesで表現したい場合、

(values)

ではなく

(values &rest nil)

となります。

(values)だと何でも許容する型という意味になるので、 全く逆の意味として認識されることになるのです。

とはいっても、&rest tが指定されていて困ることはあまりないでしょう。 厳密にoptimizerを利用したい場合は、 &rest nilを指定しないとうまく行かないかもしれません。

最後に、型values&allow-other-keysについて話します。
ANSI Common Lispの規格書には、 型valuesには&allow-other-keysが指定できると記載されています。

http://clhs.lisp.se/Body/t_values.htm

Type Specifier VALUES
values value-typespec
value-typespec::= typespec* [&optional typespec*] [&rest typespec] [&allow-other-keys] 

つまりこんな感じ

(values integer &allow-other-keys)

しかし型valuesの引数に&keyの指定は許されて無いので、&allow-other-keysは間違いじゃないかと言われています。

https://www.cliki.net/Issue%20VALUES-%26ALLOW-OTHER-KEYS

Issue VALUES-&ALLOW-OTHER-KEYS
Problem Description:
(values &allow-other-keys) matches the syntax for the VALUES type specifier, 
but the description doesn't say what it means. Because the syntax does not allow &key, 
&allow-other-keys was probably a mistake.

たぶんそのとおりであり、間違いなのでしょう。
引数&allow-other-keysは使わないでおいたほうがいいと思います。