多値関数

[id:m-hiyama:20060209:1139450736]さんのところで、多値関数に関する議論が進んでいる。
多値関数というと、コメント欄でも指摘されていますけど、Perlがありますね。

sub f {
  return $_[0]*2, $_[1]*2;
}

みたいに書けば、引数を2つ取って、それらを2倍して返すという "2-in 2-out" の関数になります。Rubyも同じように書けるようです。(一方、PHPは多値を返すにはarrayで包んであげる必要があるようです。)
関数型言語では、多値を返せない言語のほうが少数派というか、そんなのあるの?という感じ。たとえば、Concurrent Cleanでは、上のPerlの関数は、こんなふうに書きます。

f a b = (a*2, b*2)

Cleanでは、そもそも一意型が多値を返せることが前提の仕組みなので、多値が返せなければ、言語が成立しないことになってしまいます。


ところで、多値関数という議論を、関数の対称性*1(引数が多値なら返値も多値)という観点から考えると、関数から複数の値が返るというだけでは足りないような気がします。
先の「引数を2つ取って、それらを2倍して返す」という関数 f を考えると、関数が対称なら以下のような適用が可能なはず。

(a, b) = f( f( 1, 2)) // 結果は (4, 8)

しかし、Cleanは、このような適用はできません。なぜなら、関数 f は、"Int"型の値を2つ取って、"(Int, Int)"型の値を1つ返す関数だからです。型が合わないのです。
では、Perlはというと、適用可能です。つまり、Perlの関数は対称といえるのですが、文法をよく考えると、実は、Perlの関数は引数を1つしか取っていないのです。Perlの関数の引数は、文法上"@_"という配列に格納されて渡されます。ということは、Perlの関数は、本質的には "1-in 1-out" と考えるべきなのではないかと思います。


ということで、関数の対称性まで視野に入れた多値関数は知らない。多分、最近のLisp系にはあると思うのだが、その動作を確認したことはない。
そういえば、Webを巡回してみて、以下のページとその関連ページは多値を考える上では、一度読んでおいた方がいいと思った。
http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3A%C2%BF%C3%CD

*1:この言葉が適切かはよく分からないですが