Boo のスコープ
Boo には言語コアな機能として、(メソッド内などで)ローカルスコープを導入する方法がありません。
例えば、C++やJavaなんて方面の言語は { } を使って、次のようにローカルスコープを導入できます。
int a = 5;
if ... {
int a = 3; // シャドウイング。外側の a とは違う。
int b = 9;
cout << a << endl; // 3
}
cout << a << endl; // 5
// cout << b << endl; // コンパイルエラー。b は if の内側の変数。
Boo は、上記のC++コードを真似しても結果が違います。
a as int = 5
if ...:
# a as int = 3 # コンパイルエラー... ;_;
a = 3
b as int = 9
print a # 3
print a # 3
print b # 9 です。内側も外側もないです。
そこで、Boo には preserving というローカルスコープ「っぽいもの」を導入するマクロがあります。
これがスゲーかっこ悪い。
a as int = 5
preserving a: # このブロック抜けたら a の値が復帰する。
# a as int = 3 # これはやっぱりコンパイルエラー
a = 3
print a # 3
print a # 5
超カッコ悪いです。なんか、preserving というマクロの後に保存する変数名書いたりするのが、php の無名関数で use とかわざわざ書かせているみたいです。
まぁ、ブロックにスコープ機能が無いので、次のような場合に1行得する、とかイマイチお得に感じられないメリットもあります。
# if で分岐して変数 a に値設定。
# この行に a as int のような宣言が必要無い、という微妙なお得度。
if ....:
a = 5 # Boo は代入が宣言を兼ねられる。これもばっちいですね。
else:
a = 3
print a # 分岐処理の結果
preserving の正体
preserving は次のマクロです。もう想像通り。ソースは Boo 処理系の src/Boo.Lang.Extensions/Macros/PreservingMacro.boo から。
macro preserving:
restoration = Block() # 指定した変数を復帰する代入文を入れるブロック。
for arg in preserving.Arguments:
# テンポラリ変数を作って、指定した変数値を保存するコード展開
temp = ReferenceExpression(Context.GetUniqueName("preserving"))
yield [| $temp = $arg |]
# 復帰ブロックに、保存した値を再代入するコード追加。
restoration.Add([| $arg = $temp |])
yield [|
try:
$(preserving.Body) # preserving の本体
ensure:
$restoration # 本体の処理おわったら、復帰ブロック実行。
|]
まぁ、この方法を拡張して、preserving のブロックをネストメソッド(Boo はメソッドの中でメソッドを定義できます)として定義すれば、それなりにちゃんとしたスコープを導入することはできます..。
でも、初めっからちゃんとコアな機能としてほしいよね...。
結論
Boo って結構汚い言語です。それが Boo クオリティ。
といいながら、この preserving はさすがに使ったことありません。