#:g1: 対称性にこだわるGLSと継続と多値

Posted 2018-11-14 20:02:05 GMT

前回、多値についてCommon Lisp以前のLisp Machine Lispから眺めてみました。

色々な資料を眺めている中で、GLS(Guy L. Steel Jr.氏)の多値についてのスタンスが、使い勝手の方に舵を切ったCommon Lispと美しさを取ったSchemeを象徴するように見えたので、GLSと多値について書いてみたいと思います。

タイトルに継続と多値と入っていますが、GLSが多値と継続について語っているのを見付けただけです。継続に興味ある方々には、釣りタイトルみたいになってしまってすいません :)

初期のLisp Machine Lispの多値とGLS

初期のLisp Machine Lispの多値の高レベルAPIにはmultiple-value-listmultiple-value、があり、returnは多値を返せました。
multiple-valueはCommon Lispでは、命名が良くないということで、multiple-value-setqと改名されましたが、当初は代入フォームがメインだったようです。

multiple-value-bindのような束縛構文はというと、調べた限りでは、GLSが提案したものが最初で1977-03-07のメールに詳細が書いてあります。
名前は、Common Lispと同じmultiple-value-bindです。

GLSが提案した形式は、

(MULTIPLE-VALUE-BIND <function call>
                     <bindings>
                     <body>)

というもので、現在のものとは、変数と多値を返すフォームの位置が反対になっています。

この提案では、束縛部ではオプショナル引数や残余引数の指定ができることが示唆されています。

(MULTIPLE-VALUE-BIND (FOO A B C)
        (VAL1 VAL2 &OPTIONAL VAL3) ...)
or
(MULTIPLE-VALUE-BIND (FOO A B C D E)
        (VAL1 VAL2 &REST VAL3) ...)

さらに、入出力の多値の数をしっかり合せたい場合、

(MULTIPLE-VALUE-BIND (BAR B C)
        (G0001 &REST G0002)
        (FOO A G0001 D E))

と書けることを挙げて、

This idea lends a nice symmetry to the passing of arguments and
the returning of values.  Finally, one might note that this also
allows the possibility of specifying that NO values are expected back.
PROG could evaluate statements in this mode, while evaluation
of subforms should require at least one return value.
This lends itself to a better theory of statements vs. forms.

と締めています。
多値の入出力の対称性を実現でき、さらに発展して値を返すフォームを式、返さないフォームを文とできる、ということみたいです。

この提案に反応があったのか無かったのか記録には残っていないのですが、結局この仕様はそのままは取り入れられず、Common Lispでもお馴染の形式のものが1979年に導入されています。

初期のCommon Lisp仕様策定と多値とGLS

時は流れて1981年。初期Common Lispの仕様の議論ですが、当時のmultiple-value-bindの仕様(=現在のCommon Lispの仕様)を少し変更して変数部をlambdaと同じにしたらどうか(つまり上述の1977の仕様と同じ)と提案しています。

I propose that the list of variables be completely identical in syntax
to a LAMBDA-list.  This includes the use of &OPTIONAL and &REST.  One
can get precisely the current functionality by inserting &OPTIONAL at
the front of the variables list.  In addition, one would be able to get
non-NIL default values; a list of some of the values, using &REST;
and better error checking, because one can *require* that certain values
be delivered.

現在のmultiple-value-bindと何が違うかというと、GLSのものは多値の数をきっちり合せることが前提のインターフェイスになっていて、合わなければエラーになるというところです。

(defmacro gls-multiple-value-bind (args mv-form &body body)
  `(multiple-value-call (lambda ,args ,@body) ,mv-form))

(gls-multiple-value-bind (a b) (values 0 1 2) (list a b)) ;error> got 3 args, wanted 2. (gls-multiple-value-bind (a b &optional c) (values 0 1) (list a b c))(0 1 nil)

この提案に対してのDavid Moon氏からの回答が「実用指向のCommon Lisp™」という感じなのですが、実際の所、多値を返す殆どの関数は、それを使う側がいらない多値は捨てるものという前提で運用されていて、理論的に考えられるような対称性はみられない、として切り捨てています。

This sounds superficially plausible and has been proposed many times before.
The reason it has never been accepted is that in practice most functions 
which return multiple values rely on the feature that extra values the
caller does not want are thrown away.  Calling and returning aren't really
as symmetric in practice as they are in theory.  It probably would be useful
to have a way of requiring at least a certain number of values to be
returned.  Of course, then all the functions which rely on extra values
being supplied NIL would have to be fixed.  This is pretty common practice
when you have code like (and <condition> (values <val1> <val2>)).

Schemeと多値と継続とGLS

また時は流れて、1988年、Schemeメーリングリストでのことですが、GLSがSchemeの会合で継続を踏まえた多値の扱いについて話をするつもり、という旨のメールを書いています。

このメールでは、数あるLisp方言で、それぞれ多値の返却/受取で数が合わない場合の対処も違うけれど、継続と多値の問題と見做せるとしています。
また、継続は返却/受取の数は一致していることを基本とし、その上で継続に多値の数が一致しているかを問合せるaccepts?というものを提案しています。

accepts?と使うとCommon Lispの多値のような挙動は下記のように書けるようです。

;;; cwcc = call-with-current-continuation
(define (values . r)
  (define (ignore-excess-values k z)
    (if (accepts? k (length z))
    (apply k z)
    (if (null z)
        (supply-default-falses k r)
        (ignore-excess-values k (cdr z)))))
  (define (supply-default-falses k z)
    (if (accepts? k (length z))
    (apply k z)
    (supply-default-falses k (cons '#f z))))
  (cwcc (lambda (k) (ignore-excess-values k r))))

さらに、beginset!は0個の値を返すのはどうかという話もでてきますが、これも上述の1977年のアイデアですね。

まとめ

多値の返却/受取の個数合せについて、使い勝手の良い挙動でまとめたCommon Lispと、あるべき挙動を選択したScheme、そして時代と方言を越えて一貫した主張をしていたGLSを眺めてみました。

Schemeで多値と継続についての議論がどの辺りからあるのかは調べていないのですが、発表する内容として考えられていたことからしても1988年のGLSの提案が割と初期のものなのではないでしょうか。
ちなみにR7RSでもaccepts?のようなものは仕様には取り入れられていないようです。

Common Lispでは、失敗/成功のフラグをアドホックに多値の二値目で表現するようなことをします。
まさに、多値が導入された当初の目的で使っている感じですが、数合わせしないといけなくなるとすると、ちょっと面倒ですね。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus