メソッド呼び出し
Wrenは徹底したオブジェクト指向言語なので、多くのコードはオブジェクトのメソッドを呼び出すもので、通常は次のような形になります。
System.print("Heyoo!") //> Heyoo!
レシーバーとなる式(ここではSystem)に続けて、.、名前(print)、丸括弧で囲んだ引数リスト(("Heyoo!"))を書きます。引数が複数ある場合は、コンマで区切ります。
list.insert(3, "item")
引数リストは空でもかまいません。
list.clear()
VMは、次の手順でメソッド呼び出しを実行します。
- レシーバーと引数を左から右へ評価する。
- レシーバーのクラスでメソッドを探す。
- 引数の値を渡して、そのメソッドを呼び出す。
シグネチャ #
ほかの多くの動的型付け言語とは異なり、Wrenではシグネチャが違っていれば、同じ名前のメソッドを一つのクラスに複数定義できます。シグネチャには、メソッド名と受け取る引数の数が含まれます。専門的に言うと、引数の個数によるオーバーロードが可能です。
例えば、Randomクラスには、乱数の整数を得るメソッドが二つあります。一方は最小値と最大値を受け取り、その範囲の値を返します。もう一方は最大値だけを受け取り、最小値に0を使います。
var random = Random.new()
random.int(3, 10)
random.int(4)
PythonやJavaScriptのような言語では、どちらも何らかの「省略可能な」パラメーターを持つ一つのint()メソッドを呼び出します。メソッドの本体で渡された引数の数を調べ、制御フローを使って二つの動作を処理します。つまり、最初のパラメーターは「別のパラメーターが渡されていなければmax、渡されていればmin」を表します。
この種の「可変引数」のコードは理想的ではないため、Wrenでは推奨していません。
Wrenでは、これらはint(_,_)とint(_)という、完全に別々の二つのメソッドへの呼び出しです。省略可能なパラメーターも、場合分けのための制御フローも必要ないので、このような「オーバーロード」を定義しやすくなります。
実行も速くなります。渡す引数の数はコンパイル時に分かるため、適切なメソッドを直接呼び出すコードを生成でき、「引数が二つならこうする……」という実行時の処理を避けられます。
ゲッター #
オブジェクトが保存しているプロパティーや、計算して求めるプロパティーを公開するためのメソッドがあります。これらはゲッターで、丸括弧を付けません。
"string".count //> 6
(1..10).min //> 1
1.23.sin //> 0.9424888019317
[1, 2, 3].isEmpty //> false
ゲッターは、空の引数リストを持つメソッドと同じではありません。()はシグネチャの一部なので、countとcount()はシグネチャが異なります。Rubyの省略可能な丸括弧とは違い、Wrenはゲッターをゲッターとして、()メソッドを()メソッドとして呼び出すことを求めます。次の呼び出しは使えません。
"string".count()
[1, 2, 3].clear
パラメーターが不要なメンバーを定義する場合は、ゲッターにするか、空の()パラメーターリストを持つメソッドにするかを決める必要があります。一般的な指針は次のとおりです。
- オブジェクトを変更するか、何らかの副作用があるなら、メソッドにする。
list.clear()
- メソッドが複数の引数個数に対応するなら、ほかの版と一貫するよう、引数がゼロの場合も
()メソッドにする。
Fiber.yield()
Fiber.yield("value")
- それ以外なら、おそらくゲッターにできます。
セッター #
ゲッターは、読み取れる公開「プロパティー」をオブジェクトに持たせます。同様に、セッターはプロパティーへの書き込みを可能にします。
person.height = 74 // Grew up!
=があっても、これはメソッド呼び出しの別の書き方にすぎません。言語の立場から見ると、上の行は単にpersonのheight=(_)メソッドに74を渡して呼び出しています。
=(_)はセッターのシグネチャに含まれるため、オブジェクトに同じ名前のゲッターとセッターを定義しても衝突しません。両方を定義すると、読み書きできるプロパティーを提供できます。
演算子 #
Wrenには、なじみのある演算子の大半が、同じ優先順位と結合性で備わっています。前置演算子は三つあります。
! ~ -
これらは、ほかの引数を渡さずにオペランドのメソッドを呼び出すだけです。!possibleのような式は、「possibleの!メソッドを呼び出す」という意味です。
両側にオペランドを持つ、中置演算子も多数あります。次のとおりです。
* / % + - .. ... << >> < <= > >= == != & ^ | is
前置演算子と同様、これらもすべて、メソッド呼び出しの変わった書き方です。左のオペランドがレシーバーで、右のオペランドを引数として渡します。したがって、a + bは意味上、「aの+(_)メソッドにbを渡して呼び出す」と解釈します。
-には前置演算子と中置演算子の両方がある点に注意してください。シグネチャが-と-(_)で異なるので、両者に曖昧さはありません。
これらの大半は、すでになじみがあるでしょう。..と...は「範囲」演算子です。数値型は、範囲オブジェクトを作成するためにこれらを実装していますが、ほかの演算子と同じように、メソッド呼び出しです。
isキーワードは「型の検査」の演算子です。基底のObjectクラスがこれを実装し、オブジェクトが指定したクラスのインスタンスかどうかを調べます。必要になることはまれですが、自分のクラスでisをオーバーライドすることもできます。これは、特定のクラスに見せかけたいモックやプロキシーなどに役立ちます。
添字 #
数学でおなじみの別の構文に、角括弧([])による添字があります。コレクションのようなオブジェクトを扱う際に便利です。例えば、次のように使います。
list[0] // Get the first item in a list.
map["key"] // Get the value associated with "key".
もうお分かりでしょう。Wrenでは、これらもメソッド呼び出しです。上の例でのシグネチャは[_]です。添字演算子には複数の引数も渡せるため、多次元配列などに便利です。
matrix[3, 5]
これらの例は添字の「ゲッター」ですが、対応する添字セッターもあります。
list[0] = "item"
map["key"] = "value"
これらは、シグネチャが[_]=(_)で、添字(一つまたは複数)と右辺の値を引数に取るメソッド呼び出しと同じです。
制御フロー →← マップ