作成 2003/11/10
更新 2004/1/13
巷で話題?のD言語。さわりだけ触る。
D言語とは、新たに設計された言語です。過去にしばられず、いらないところは捨て、おいしいところは残し、新機能も取り入れています。大きなパラダイムシフトはありませんが、言語レベルでDesign By Contract 、Unit Test、強いtypedef 、auto属性といった機能がサポートされます。ここ(マニュアル)に概要が書かれています。
マニュアルが以下のページにあります(翻訳素敵)。
以下はWindows環境の例で話をすすめます。上記サイトから次のものをダウンロードします。
dmd.zipを展開し、さらにdmc.zipを展開し、上書きします。ディレクトリは次のようになります。
dmd/ +--bin/ | +--dmc.exe コンパイラ +--samples/ サンプル +.. dm/ +--bin/ +...
dmd、dmのbinにパスを通します。
set PATH=C:\dmd\bin;C:\dm\bin;%PATH%
C:\dmd\samples\d>\dmd\bin\shell all.sh shell 1.00 \dmd\bin\dmd hello C:\dmd\bin\..\..\dm\bin\link.exe hello,,,user32+kernel32/noi; hello hello world args.length = 1 args[0] = 'C:\dmd\samples\d\hello.exe' ...
ソースを書きます。ソースファイルの拡張子は.dがよいらしい。
hello.d
import c.stdio; int main( char[][] arg ) { printf( "Hello, World.\n" ); return 0; }
コンパイルします。
C:\dmd\test>dmd hello.d C:\dmd\bin\..\..\dm\bin\link.exe hello,,,user32+kernel32/noi;
実行します。
C:\dmd\test>hello Hello, World.
とりあえずは、こんなんで満足。プログラムについては、マニュアルや参考ページを見てください。
ここでは、D言語の機能について、このへんの比較を見ながら、なにがどう便利かについて調べていきたいと思います。D言語に触る目的は、このあたりを調べてみようと思ったから。
とりあえず、(自分にとって)ものめずらしい機能から。 ローカル変数にauto属性をつけると、スコープから抜けるときに、自動的にデストラクタが呼ばれます。
import stream; class Foo{ this(){ stdout.writeLine( "constructor called." ); } ~this(){ stdout.writeLine( "destructor called." ); } void doSomething(){ stdout.writeLine( "doSomething." ); } } int main( char[][] arg ) { doMethod(); stdout.writeLine( "end." ); return 0; } void doMethod(){ Foo foo = new Foo(); foo.doSomething(); }
このプログラムを実行すると、次のようになります。
C:\app\eclipse3\workspace\dtest1>dmd Foo.d C:\dmd\bin\..\..\dm\bin\link.exe Foo,,,user32+kernel32/noi; C:\app\eclipse3\workspace\dtest1>Foo constructor called. doSomething. end. destructor called.mainメソッドの終了前にendと出力しています。 FooクラスのデストラクタはGCによって呼ばれています。 これがauto属性をつけると、
void doMethod(){ auto Foo foo = new Foo(); foo.doSomething(); }
こうなります。
C:\app\eclipse3\workspace\dtest1>Foo constructor called. doSomething. destructor called. end.
メソッドから抜けるときにデストラクタが呼ばれていることがわかります。 もちろんtry〜finallyでも同じことができますが、 autoを使うとソースがとてもシンプルになりますね。 DBのコネクションやファイルなどのストリームのcloseその他、 いろいろ使えますね。5へぇ。
ちょっとより道。EclipseのD言語プラグインがあるらしいので使ってみた。 インストールは、以下からeclipseDをダウンロードして、展開、pluginsフォルダにコピーするだけです。
拡張子.dのファイルをD Editorから開くと使えます。
現状、キーワードのハイライトだけのようですが、そのうち便利になるかもしれませんね。
もうちょっとより道。Eclipseでソースファイルを保存したときに、自動的にコンパイルするようにします。簡単な方法は外部ツールビルダーを使う方法です。
ビルド用スクリプトファイルを用意します。ここでは、コンパイルコマンドを記述したbuild.batというバッチファイルを作成しました。
build.bat
set PATH=C:\dmd\bin;C:\dm\bin;%PATH% make
makefile(とりあえず版)
all: dmd src\hello.d src\Foo.d -odobj
外部ツールビルダーを登録します。
ソースファイルを保存(CTRL+S)するたびにバッチファイルが実行されます。まだファイル数は少ないですが、さすがにコンパイルが速い気がします。
なお、自動ビルドにチェックした場合は、他のプロジェクトのファイルを変更した場合もビルドが実行されてしまうようです。builderでも作るか。。。
Eclipseの寄り道を終わって、D言語にもどります。
XXUnit等のツールをもってこなくても、D言語は言語レベルで単体テストが可能です。unittestメンバーにテストコードを記述します。
class Calc{ int add(int x, int y) { return x + y; } unittest { Calc calc = new Calc(); assert(4 == calc.add(2,1));//わざと間違い } } void main(){ }
-unittestオプションでコンパイルし、実行すると、クラスロード時(?)に単体テストが実行されます。コンパイル時にテストしてくれてもいいのに?
C:\dmd\test>dmd -unittest Calc.d C:\dmd\bin\..\..\dm\bin\link.exe Calc,,,user32+kernel32/noi; C:\dmd\test>Calc Error: Assertion Failure Calc(9)
契約による設計は、プログラムの信頼性を高める手法です。Javaでは、1.4以降のassertを使って、同じようなことが実現できます。D言語でも、assertを使って、プログラムの妥当性を検査します。
さらに D言語では、in、outキーワードを使い、事前条件、事後条件についてチェックが可能です。コードはこんな感じ(マニュアルのサンプルそのまま)。mySqrtメソッドの後に "{"がないことに注意。
import math; class Hoge { float mySqrt(float x) in { assert(x >= 0); } out (result) { assert((result * result) == x); } body { return math.sqrt(x); } } void main() { Hoge hoge = new Hoge(); hoge.mySqrt(-1); }
実行すると、こうなります。事前条件(in)でアサートエラーがスローされます。
C:\dmd\test>dmd Hoge.d C:\dmd\bin\..\..\dm\bin\link.exe Hoge,,,user32+kernel32/noi; C:\dmd\test>Hoge Error: Assertion Failure Hoge.d(8)
これだけだともう一歩ありがたみは感じられませんが、さらにクラスの不変条件をチェックすることができます。不変条件はinvariantで記述します。
import stream; class Robot { int energy; invariant{ assert(0 <= energy); } this(int energy) { this.energy = energy; } void walk(){ energy--; stdout.writeLine("walk"); } } void main() { Robot robo = new Robot(5); for(int i=0; i<10; i++){ robo.walk(); } }
不変条件は、コンストラクタの後、デストラクタの前、public/exportメソッドの実行前後にチェックされます。実行すると以下の結果が表示されます。energyが0でwalkが呼ばれた後にアサートエラーがスローされます。なお、この例は不変条件を使う場面としては不適切だと思われます(単にinvariantを使う例と考えてください。いい例が思いつかなかった)。
C:\dmd\test>Robot walk walk walk walk walk walk Error: Assertion Failure Robot.d(9)
単体テスト、契約による設計とも、「ロジックもテストも一箇所(1つのクラス)に書く」というポリシーのようですね。