Posted 2018-11-12 19:54:32 GMT

Goの多値についての記事が人気のようで、この数日Twitterで多値の話題が賑わっています。

多値が話題になることなど、そうそうないですが、多値といえばやっぱりCommon Lispでしょう!、ということでLispと多値について書いてみます。

Lispと多値の歴史

多値といえばCommon Lispですが、最初の仕様のCommon Lisp(1984)でも標準の言語機能になっています。

Common Lispの人達は、普段から普通に便利に使っていますが、多値周りはシンプルなデザインなので使い方で混乱する、ということも特にないでしょう。

典型的な使われ方には下記のようなものがあります。

しかし、その多値機能ですが、Common Lispで初導入という訳ではなく、直接の祖先であるLisp Machine Lispにから輸入したものです。

ということで、Lisp Machine Lispの歴史を遡ってみましたが、私が調べた限りでは、多値が導入されたのは、1976年辺りのMIT LispマシンのCONSのようです。

LISP Machine Progress Report(1977)では、

A traditional weakness of Lisp has been that functions have to take a fixed number of arguments. Various implementations have added kludges to allow variable numbers of arguments; these, however, tend either to slow down the function-calling mechanism, even when the feature is not used, or to force peculiar programming styles. Lisp-machine Lisp allows functions to have optional parameters with automatic user-controlled defaulting to an arbitrary expression in the case where a corresponding argument is not supplied. It is also possible to have a "rest" parameter, which is bound to a list of the arguments not bound to previous parameters. This is frequently important to simplify system programs and their interfaces. A similar problem with Lisp function calling occurs when one wants to return more than one value. Traditionally one either returns a list or stores some of the values into global variables. In Lisp machine Lisp, there is a multiple-value-return feature which allows multiple values to be returned without going through either of the above subterfuges.

という風に、入力側のオプショナル引数と、出力側の多値の対で語られています。

複数の値を返したい、というニーズがそんなにあったのかは不明ですが、リストで返したり、大域変数経由で渡すところを多値機構として、すっきり纏めたという話は、それはそれで説得力があるという所でしょうか。

MIT LispマシンはSECDマシンに非常に近い構成らしいので、専用マシンは多値をハードウェア支援で高速に実現できますよ!的な記述も探してみましたが、そういう話はみつけられませんでした。残念。

興味のある方向にCONS時代のLispのマニュアルを置いておきます。

このマニュアルでは、 %CALL-MULT-VALUE や、 %RETURN-N 等のプリミティブが定義されていることが判ります。

ちなみに、Scheme方面では、恐らく1980年代前半に T が最初に導入し、その後紆余曲折を経てR5RSで仕様化されたようです。

Schemeも元を辿れば、Lisp Machine Lisp由来かなと思います。

Common Lispでの多値の扱い

多値の扱いを考える場合、N個の値を返す場合と、受けとる場合にどうなるかを考慮する必要がありますが、Common Lispでは下記のようになります。

値を返す

フォームは0個以上の値を返す

値を受け取る

関数は単値のみ受けとる 関数は二つ目以降は無視する 0個の場合、 nil を受け取る 関数以外のスペシャルオペレーター/標準マクロには個別に規定がある

詳細は、Common Lispの仕様を参照してください。

スペシャルオペレーター/標準マクロの個別の規定ですが、過剰であれば無視され、足りなければ、 nil が補われる動作と考えて問題ないと思います( multiple-value-call 以外は)。

ただこの挙動をもって、Common Lispの多値機構の挙動と見做せるかというと、そうではなく、そういう風にフォーム構成されているだけという点に注意が必要かなと思います。

受取り側が期待した個数より少なければ nil で補填され、敷衍して0個の場合は nil となるのがCommon Lispの多値機構、という訳ではありません。

例えば、 multiple-value-bind は足りない場所は nil を補い、過剰な場合は捨てますが、

( multiple-value-bind ( q r s ) ( floor 1 2 ) ( list q r s ) )

それは、多値を引数リストにする時にオプショナル引数のように処理しているからで、入出力の多値の個数が一致していなければ、エラーにすることも可能です。

また、 nil 以外のデフォルト値を設定することも可能でしょう。

( defmacro strict-multiple-value-bind ( ( &rest vars ) mv-form &body body ) ` ( multiple-value-call ( lambda ( ,@vars ) ,@body ) ,mv-form ) ) (strict-multiple-value-bind (q r s) (floor 1 2) (list q r))

まとめ

以上ですが、Common Lispでは、継続と多値があまり連続した議論になっていないのがお判りでしょうか。

このまま書き進んで行こうと思いましたが、長くなったので、続きは別の回にしたいと思います。

次回: 対称性にこだわるGLSと継続と多値

■

