左辺値代入

この記事はPerl6 Advent Calendar 2011、22日目の記事です。


さて、記事が落ちたのも二回目ですが、まぁ何とかなるでしょう。明日はクリスマスなので奇跡が起きるに違いない。


今日紹介するのは左辺値代入です。

左辺値代入とは

読者の方が全員ご存知かどうかは解りませんが、Perl5のsubstr関数は次のような書き方が可能でした。

my $s = "abcdefg";
substr($s, 2, 4) = "123";
say $s; #> ab123fg

初めて見る方には奇妙に見えるかもしれませんが、substr関数は左辺値特性を持つため、代入式の左辺に置く事が出来ます。substrの場合、代入式の右辺を第4引数に与えた時と同じ挙動を示します(「インデックスの2..4番目に対して代入を行う」と読めば解りやすいかもしれません)。

このような左辺値代入の仕組みは複雑で、Perl5でこのような関数を定義するのは少々面倒でした。定義する方法は憶えてないので適当にググってもらうとして、これがどういう場面で役立つかを考えてみましょう。


と言っても答えは簡単で、オブジェクトの属性への代入に使います。Perl5のMooseの場合、標準的には属性への値のセットは次のような書き方で実現していました。

  • アクセサメソッドに引数を与えない場合は値を返す
  • アクセサメソッドに引数を与えた場合は値を格納する

しかし、Perl6では属性への値のセットは直接代入するという文法になっていますよね。これに左辺値代入を使っているのです。つまり、属性への書き込みに自動で作られるアクセサではなく、自分で処理を挟みたい場合は左辺値代入を使ったアクセサを書く必要がある訳です。

左辺値代入できる関数を書いてみる

では実際にどうやって定義すれば良いのかを見てみましょう。例を単純にするため、左辺値代入された値を保持する関数を考えます。つまり、以下のような挙動になります。

  • 右辺値として呼び出された場合は格納した値を返す(FETCH)
  • 左辺値代入された場合は値を格納する(STORE)


では、実際にコードの例を示します。

sub lvalue_sub () is rw {
    state $val;
    return Proxy.new(
        FETCH => method {
            return $val;
        },
        STORE => method ($rvalue) {
            $val = $rvalue;
        });
}


まず、この関数が左辺値代入可能な関数である事を示すis rwトレイトを指定します。
次に、この例では値を格納するための変数としてstate変数を用意しています。stateはmyと同じ様に変数宣言に用いるキーワードで、そのブロックの中で状態(state)が保持される静的な変数を宣言できます。
最後に、Proxyオブジェクトを返すようにすれば完成です。コンストラクタにFETCHとして渡している無名メソッドが格納する場合の処理、STOREとして渡している無名メソッドが呼ばれます。STOREメソッドには引数を1つ指定し、代入される右辺値はその引数に束縛されます。


実際に実行してみましょう。

lvalue_sub = 1;
say lvalue_sub; #> 1
lvalue_sub = 2;
say lvalue_sub; #> 2

正しく動いていますね。

まとめ

  • is rwで左辺値特性を指定できる
  • Proxyを返す
    • FETCHで読み出し
    • STOREで代入