#:g1: Lispと多値

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では下記のようになります。

値を返す

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

値を受け取る

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

詳細は、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))
;=> (0 1 nil) 

それは、多値を引数リストにする時にオプショナル引数のように処理しているからで、入出力の多値の個数が一致していなければ、エラーにすることも可能です。
また、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)) ;error> got 2 args, wanted at least 3.

まとめ

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

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

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


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus