Posted at
D言語Day 1

D言語 Language Update 2014

More than 3 years have passed since last update.

D言語 Advent Calendar 2014の最初の記事です.2013年と同じく,2014年の間にあったD言語で影響のありそうな変更について書きます.

現段階でのdmdの最新バージョンは2.066.1で,2.067.0のベータがリリースされている状態です(2.067の正式リリースを待ってましたが,今年中には出なさそうなので諦めて記事を書いてます),


公式プロジェクト


dmd


複数ディメンションでのアクセス

以下のような感じで,opIndex / opIndexAssignにいくつも引数を取れるようになりました.

import std.stdio;

struct A
{
int[][] arr;

// other members

int opIndex(size_t i1, size_t i2) // 3つにしたかったらsize_t i3を足すだけ
{
return arr[i1][i2];
}
}

void main()
{
A a = A(new int[][](10, 10));
// mutate a object;
writeln(a[2,3]);
}

多次元配列を使う数値計算などで有用です.


@nogc

GCのヒープアロケーションを禁止する属性です.

void f() @nogc

{
int* a = new int(10);
}

上のような関数をコンパイルしようとすると,エラーになります.

foo.d(3): Error: cannot use 'new' in @nogc function f

@nogcに関してはmono_shooさんが書いた@nogcに関する考察も参考にしてみてください.


shared型への書き込みチェックの強化

shared int global;

void main()
{
global++;
global *= 2;
}

今まで上記のコードは許されていたのですが,2.066以降は禁止です.以下のようにcore.atomicのユーティリティ群を使う必要があります.

shared int global;

void main()
{
import core.atomic;
atomicOp!"+="(global, 1);
atomicOp!"*="(global, 2);
}


浮動小数点周りをクリーンアップ

<>=とか!<>=系のやつが全部非推奨になりました.まぁ誰も使ってなかったですし…いずれ削除されます.

min_normalを使ってください.これらもいずれ削除されます.


CTFE時のunionの限定的なサポート

自分で良い例を考えるのが面倒だったので,ChangeLogのサンプルコードを引用します.

union U

{
size_t x;
int* y;
}

bool test()
{
U u;
assert(u.x == 0); // In here, reading u.y will cause CTFE error.

u.y = [1,2,3].ptr; // Writing value to overlapped field u.y will make corresponding field u.x invalid.

assert(u.y[0..3] == [1,2,3]); // u.y holds valid field and reading it is allowed
// In here, reading u.x will cause CTFE error.

u.x = 10; // Set value to u.x again.
assert(u.x == 10); // OK
// In here, reading u.y will cause CTFE error.

return true;
}
static assert(test()); // run CTFE

コメントに書いてある通りです.あるフィールドを弄っている時に他のフィールドを読み込んだりするのはダメなんですが(つまりunionを使ったビット操作はダメ),ちゃんとそれぞれにわけてアクセスすれば動くようになりました.

これによって,いくつかのunionベースのライブラリがコンパイル時に動くようになっています.


新しいtrait


  • getFunctionAttributes: 関数についている属性が取得出来ます.コンパイル時に特定の属性を持った関数でラップしてコンパイル出来るかどうか?みたいなトリックはもういりません

  • getAliasThis: alias thisで使われているフィールド名を返します.現状必ず長さは1ですが,DIP66が採択されたので,いずれは複数の値を返すようになると思います.

  • isTemplate: あるシンボルがtemplateかどうかチェック出来る.


inout const

昔はimmutable(T)inout(T)const(T)になってたんですけど,これだとinout情報が消えてしまうので,inout constが増えました.ちょっと型が増えちゃったなぁという感じで個人的には微妙なんですが,この辺D3で整理されるのかどうか…


コンパイラオプションの変更


  • -noboundscheck: 非推奨になりました.boundscheck=on/safeonly/offと指定するようになります.-releaseだとsafeonlyがデフォルトです

  • -vgc: ヒープアロケーションが起きるところを列挙してくれます.例えば以下のような感じ


    • foo.d(3): vgc: 'new' causes GC allocation



  • -vcolumns: 行数だけじゃなくて,何文字目でエラーが起きているかもおしえてくれます!


    • foo.d(3,14): vgc: 'new' causes GC allocation




druntime


配列/連想配列の組み込みプロパティを関数で置き換え

配列のdup/idup,連想配列のrehash/dup/byKey/byValue/keys/values/getは今まで組み込みのプロパティだったんですが,D言語の型や属性の扱いが改善され,通常の関数でも実現出来るようになりました.なので,プロパティから通常の関数で実装しなおしました.UFCSのおかげで今までと同じようにアクセス出来るので,ユーザ側には何も影響がありません.


core.time.Durationの改善

Duration.get and Duration's individual unit getters are bug-prone

時間関係のコードを書いた人であれば嵌まったことがあるかもしれませんが,corea.time.Durationにはある単位で値を取得するためのgettotalの二つの関数がありました.

assert(dur!"hours"(49).total!"days" == 2);

assert(dur!"hours"(49).total!"hours" == 49);

assert(dur!"hours"(49).get!"days"() == 2);
assert(dur!"hours"(49).get!"hours"() == 1);

挙動の違いは上のコードを見て貰えれば分かると思うんですが,totalがある単位を基準にして取得するのに対して,getはある単位でのみ有効になっている値だけを取得します.このgetが名前が汎用的過ぎる上に間違った使い方を誘発するので非推奨にして,よりわかりやすいsplitが追加されました.

auto d = dur!"days"(12) + dur!"minutes"(7) + dur!"usecs"(501223);

long days;
int seconds;
short msecs;
d.split!("days", "seconds", "msecs")(days, seconds, msecs);
assert(days == 12);
assert(seconds == 7 * 60);
assert(msecs == 501);

auto splitStruct = d.split!("days", "seconds", "msecs")();
assert(splitStruct.days == 12);
assert(splitStruct.seconds == 7 * 60);
assert(splitStruct.msecs == 501);

splitの使い方は上のテストコードを見れば分かると思います.引数を指定した場合には,その引数にそれぞれの単位での値が割り当てられます.引数がなければ構造体が返ってくるので,欲しい単位のメンバにアクセスすれば良いです.

getよりはAPIとしてはわかりやすくなっていると思います.


phobos


std.algorithm



  • any/all/canFind/equalのtemplate化

これによって,他の関数のpredicatorになれるようになり,aliasを使って簡単に新しいpredicatorを定義出来るようになりました.

void main()

{
string[] inputs = ["foo1", "bar2"];

bool allContainDigit;
foreach (input; inputs)
{
if (!any!isDigit(input))
{
allContainDigit = false;
break;
}
}
}

今まで上のように書いていたものが,下のようによりシンプルに書けるようになった.

void main()

{
string[] inputs = ["foo1", "bar2"];
bool allContainDigit = all!(any!isDigit)(inputs);

// ある関数をaliasでwrapするのも簡単
alias isAnyDigit = any!isDigit;
}



  • predSwitch, sumの追加

string res = 2.predSwitch!"a < b"(

1, "less than 1",
5, "less than 5",
10, "less than 10",
"greater or equal to 10");

assert(res == "less than 5");

sumは名前の通り,Rangeの各要素を足してくれるやつです.


std.digest.sha

SHAに関しては今まではSHA1だけでしたが,他のSHA256やSHA512などもサポートされました.


ARM対応

druntimeも含めてなんですが,ちょくちょく「ここがARMだと上手く行かない」みたいなパッチが飛んできてはマージされてました.去年あたりからD言語をAndroid上で使いたい人が色々とやっているようなので,いずれはD言語もgdc使ってAndroidで動くんじゃないかと思います.


InPlace版の追加,コードのimmtabule/pure/CTFE/@nogc対応,etc

今年はPhobosそのものに大きなモジュールの変更はそんなにありませんでした.上で紹介したような機能追加は勿論様々なモジュールでありましたが,どちらかというとバグ修正や最新機能への追従が多かった気がします.CTFEで動かなかった関数のいくつかは動くようになりましたし,immutable/pure/@nogc化,メモリをなるべく使わないようにInPlace版の追加,その他Range周りの対応など,大量の細々とした変更がメインだったなぁと思う年でした.

std.logger(std.logが死んで代わりに出てきたやつ),std.serialization,std.lexerなどのモジュールがReviewキューに貯まっているので,この辺は来年辺りに進展があるんじゃないかなぁと思ったりしてます.


その他のプロジェクト / イベント


ベータテスティングの改善

Beta Testing

ベータリリース周りがかなり改善されました.専用のページが出来ましたし,BlockerやRegressionもページ上で表示されているので,何が問題かすぐわかるようになってます.


D Cookbook

D言語界のライブラリ製造器の一人であるAdamさんの本がPackt Publishingから出てました.実はまだ読んでないので,時間を見つけて読む!


D言語ユーザ

Facebookは相変わらずD言語ユーザなんですが(Facebook製のPrestoのODBCドライバはD言語製),その他にもD言語でMQTTクライアントを書いて使っている所があったり,Adrollという有名なアド系の会社の人がデータ処理でつかっていたりと,コツコツユーザが増えてる感じはします.

データ解析や自然言語処理や機械学習とかには,個人的にはD言語はかなり向いているとは思うので,今後この辺はちょくちょく増えるのではないかと思ったりはしてます.


DConf 2014

今回は予定があわなくて参加出来なかったんですが,DConfがまた行われました.今回はScott Meyersも参加したりと,前よりさらに大きくなったようです.

DConf 2015?

DConf 2015もやるらしいので,時間があえば参加したい所です.あとSVでD言語のミートアップが開催されるらしく,少し活発になってきてますね.


まとめ

一年振り返ってみました.破壊的変更に関してはもう完全にRustに譲ってしまって,かなり機能追加・改善の方に開発の流れが行ってます.決して変更・削除が行われないわけではないですが,以前よりも格段に減りました.ここでは言及しませんでしたが,gdcやldcなどは相変わらず元気で,お互いパフォーマンスを競いあったりしてます.

来年はどの方向に舵を切っていくのか,楽しみですね.