this 引数についてちょっと書いてみる

昨日だか一昨日だかの記事でちょっと出てきた式について解説してみようかなと思い立った。

puts@ obj;

これは、変数 obj に入っているオブジェクトの文字列表現を標準出力に出す式です。これに良く似た式は、iolanguage の println メソッドが該当します。this オブジェクトを標準出力するメソッドです。

obj println

しかし厄介なことに、この式は変数 obj に何もオブジェクトが入っていなかったときに破綻してしまうのです。


kuzha では、何もオブジェクトが入っていない変数を参照しようとすると void という特別な値を返します。残念ながら void はオブジェクトではありません*1。そのため puts メソッドを起動することが出来ず、VoidOperationException が投げられてしまいます。

kz> void puts;
  >
kuzha.VoidOperationException

kuzha では、このような自体を回避するために、C# のように ?? 演算子が使えるようになっています。?? 演算子は、左辺が void または null でないときのみ左辺の値を取り、そうでないときには右辺を評価し全体の値とする演算子です。この演算子はメソッド呼び出しより優先順位が高いので、次のように書くことが出来ます。

kz> void ?? "[void]" puts;
  >
[void]

さて、ところで単に puts というコードを与えた場合、何が起きるのでしょうか。

kz> puts;
  >
LOBBY(0x18A992F)

kz> this puts;
  >
LOBBY(0x18A992F)

以下のように、現在の this が puts されます。this puts と同じ結果が得られますが、厳密には puts と this puts は異なるプログラムです。

// local.puts を this = LOBBY(0x18A992F) として呼び出す
kz> puts;
  >
LOBBY(0x18A992F)

// this.puts を this = LOBBY(0x18A992F) として呼び出す
kz> this puts;
  >
LOBBY(0x18A992F)

local と this の puts メソッドは両方とも同じものを指しますので、this を揃えてあげれば同じような動作を行う、という仕掛けです。


さて本題。kuzha には this の値を自由に指定してメソッドを呼び出すという @ 引数という構文があります。それを用いて puts と同じ動作を行うプログラムを書くと、次のようになります。

// local.puts を this = LOBBY(0x18A992F) として呼び出す
kz> puts @ this;
  >
LOBBY(0x18A992F)

// local.puts を this = LOBBY(0x18A992F) として呼び出す
kz> local puts @ this;
  >
LOBBY(0x18A992F)

// 参考:local.puts を this = local = #scope(0x1BC4459) として呼び出す
kz> local puts;
  >
#scope(0x1BC4459)

メソッド呼び出し先の this を指定した状態でメソッドを起動しているのです。
実はこの @ 引数には、void 値も指定することが出来ます。これが最初に出てきた式の意味です。

// local.puts を this = void として呼び出す
kz> puts@ void;
  >
void

今度は void そのものを puts することが出来ました。メソッド自体は local.puts を起動しているので、VoidOperationException もスローされません。

まとめ

puts@ obj;

この式は、local.puts を this = obj として呼び出す式です。
変数 obj に何も入っていない場合には、obj = void となり、標準出力には "void" と印字されます。

*1:kuzha 上で扱える値のうち、唯一オブジェクトではない値です