Squirrel 言語は、 C++ 言語で書かれた、組み込み向けのスクリプト言語のインタプリタです。主にゲームのカスタマイズに使われていますが、きわめてマイナーな存在であり、競合する Lua のほうがはるかに有名です。
しかし私は個人的には Squirrel 言語のほうを好んで使っています。理由としては、
- より直観的なクラス定義構文
- C, C++, Java に近い構文
- 間違った代入を防ぐフィールド追加演算子 (<-)
- (比較的) 厳格な型の管理
たとえば3次元のベクトルを表すクラスの基本的な定義は次のように書け、 C++, Java プログラマにとっては非常に直感的です。
class Vec{ x = 0; y = 0; z = 0; constructor(x_ = 0, y_ = 0, z_ = 0){ x = x_; y = y_; z = z_; } }
これと同じことを Lua で書くと次のようになります。
Vec = { x = 0, y = 0, z = 0 } function Vec:new (x,y,z) o = {} setmetatable(o, self) self.__index = self o.x = x o.y = y o.z = z return o end
信じられますか?テーブルの記述は { } で囲っているのに対し、関数定義は function end で囲むという仕様です。どうしてこんな非対称な言語仕様で良いと思えるのか理解できません。(個人的には Pascal, Fortran 系の begin/end で囲むというブロックの記法の視認性が良いと思えたことは一度もありません。)
しかも Lua にはクラスとインスタンスの区別がないので、メタテーブルを手でセットして継承関係を表現しなければなりません。 JavaScript のプロトタイプベースの継承と同じで、初見では何をやっているのか全く分かりません。
これに対し、 Squirrel の継承は次のように極めてリーダブルです。
class Base{ constructor(){ /* ... */ } } class Derived extends Base{ constructor(){ /* ... */ } }
また、 JavaScript や Lua や Python (他にも多くのスクリプト言語が該当するでしょうが)では、オブジェクトへのフィールドの追加が代入だけでできてしまいます。次のは JavaScript の例です。
しかしこの仕様だと、スペルミス等で意図せずにフィールドを作ってしまうようなバグの検出はとても困難です。
これに対し、 Squirrel ではフィールド追加演算子 (<-) を使わないでテーブルに存在しないフィールドに代入しようとしてもエラーになります。
演算子の美的センスには疑問に思うところもありますが、より保守性を重視した言語設計と言えます。
また、 Squirrel ではインスタンスを一つでも作ると、クラスへのフィールドは追加できなくなります。
このことによって、一つのクラスの全てのインスタンスは同じフィールド名を共有していることが保証されます。
また、 JavaScript や Lua は型に関してはあまり厳格ではなく、テーブルと配列の区別がなかったり、整数型と浮動小数点数型の区別があいまいだったりします。この特性は、その場で書き捨てのスクリプトを書くには便利ですが、数千行以上のある程度の規模のプロジェクトにおいては保守性がむしろ低下します。
この点についても Squirrel は厳格であり、配列の定義には [ ] 、テーブルの定義には { } と演算子が決まっており、内部的な型も区別されます。これは組み込み向け軽量スクリプト言語としては比較的珍しい思想です(完全な静的型付けスクリプト言語 AngelScript というさらに珍しいものもありますが。)
以上のように、 Squirrel は、 C++ や Java のような静的型付け言語に近い性質を持っており、型についてより厳格です。
しかし、不満な点もないわけではありません。フィールド追加演算子 (<-) の見た目が代入という操作からかけ離れすぎているのも気になりますし、ローカル変数の定義においては初期化に代入演算子 (=) を使わなくてはいけないというのも非対称です。
さらに、ネストされた関数の解釈も直感的とはいえません。
次の例では、外側の outer 関数の中で内側の f 関数を定義しているのですが、この f 関数は outer 関数よりも外のコードからも参照できてしまいます。
これは JavaScript とは異なります。 JavaScript では内側の関数は定義した外側の関数の中でしか使えません。その方がプログラマの意図には沿っている可能性が高いでしょう。
総合的に言って、生産性と型の安全性のバランスの取れたスクリプト言語として私は Squirrel を高く評価しています。大きな欠点としては知名度の低さとコミュニティの未成熟なところ(あとドキュメントが素っ気なさすぎるところとか…)がありますが、猫も杓子も GitHub という今の時代にあって、ホストされることによって改善されてくれれば良いですね。
var t = {} t.x = 1;
しかしこの仕様だと、スペルミス等で意図せずにフィールドを作ってしまうようなバグの検出はとても困難です。
var someTable = { someField: 1 } someTable.somefield = 2; alert(someTable.someField); // 1 !!!プロジェクトの規模が大きくなればなるほど、このような"自由な"言語仕様はボトルネックになってきます。
これに対し、 Squirrel ではフィールド追加演算子 (<-) を使わないでテーブルに存在しないフィールドに代入しようとしてもエラーになります。
local t = {} t.a = 1; // ERROR! t.a <- 2; // OK! t.a = 2; //OK!
演算子の美的センスには疑問に思うところもありますが、より保守性を重視した言語設計と言えます。
また、 Squirrel ではインスタンスを一つでも作ると、クラスへのフィールドは追加できなくなります。
class A{ a = 1; } A.b <- 2; // OK! local a = A(); A.c <- 3; // error!
このことによって、一つのクラスの全てのインスタンスは同じフィールド名を共有していることが保証されます。
また、 JavaScript や Lua は型に関してはあまり厳格ではなく、テーブルと配列の区別がなかったり、整数型と浮動小数点数型の区別があいまいだったりします。この特性は、その場で書き捨てのスクリプトを書くには便利ですが、数千行以上のある程度の規模のプロジェクトにおいては保守性がむしろ低下します。
この点についても Squirrel は厳格であり、配列の定義には [ ] 、テーブルの定義には { } と演算子が決まっており、内部的な型も区別されます。これは組み込み向け軽量スクリプト言語としては比較的珍しい思想です(完全な静的型付けスクリプト言語 AngelScript というさらに珍しいものもありますが。)
以上のように、 Squirrel は、 C++ や Java のような静的型付け言語に近い性質を持っており、型についてより厳格です。
しかし、不満な点もないわけではありません。フィールド追加演算子 (<-) の見た目が代入という操作からかけ離れすぎているのも気になりますし、ローカル変数の定義においては初期化に代入演算子 (=) を使わなくてはいけないというのも非対称です。
さらに、ネストされた関数の解釈も直感的とはいえません。
次の例では、外側の outer 関数の中で内側の f 関数を定義しているのですが、この f 関数は outer 関数よりも外のコードからも参照できてしまいます。
function outer(){ function f(x){ return x*x; } f(1); // OK! } outer(); f(2); // error!
これは JavaScript とは異なります。 JavaScript では内側の関数は定義した外側の関数の中でしか使えません。その方がプログラマの意図には沿っている可能性が高いでしょう。
総合的に言って、生産性と型の安全性のバランスの取れたスクリプト言語として私は Squirrel を高く評価しています。大きな欠点としては知名度の低さとコミュニティの未成熟なところ(あとドキュメントが素っ気なさすぎるところとか…)がありますが、猫も杓子も GitHub という今の時代にあって、ホストされることによって改善されてくれれば良いですね。
0 件のコメント:
コメントを投稿