Edited at

Dart 1.9.1リリースノート

More than 3 years have passed since last update.

最初に言っておきます。

Dartは死んでません。ただの方針転換です

これが負け惜しみでないことは以下のリリースノートを見てわかってもらえれば幸いです。


1.9.1リリース!

今朝、待望のDart1.9がリリースされました。リリースノートはこちら

今回のアップデートはECMA標準に制定した仕様の実装が完遂された記念すべき内容です。具体的には


  • async/awaitの正式サポート

  • enumの正式サポート

が含まれています。その他にも多くの変更が盛り込まれているので1つずつ紹介します


言語仕様の変更


async/awaitサポート

ついにasync/awaitが構文レベルでサポートされました。使用可能になった予約語は以下の6つです。


  • async

  • await

  • sync*

  • async*

  • yield

  • yield*


Future & Stream : async, await

asyncawaitはFuture APIとStream APIを簡単に記述するための構文です。asyncのマーキングをつけた関数の中でawaitを使うことが出来ます。

checkVersion() async {

var version = await lookUpVersion();
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
}

このコードは次のFuture APIをそのまま使ったものと同義です


checkVersion() {
lookUpVersion().then((version){
if (version == expectedVersion) {
// Do something.
} else {
// Do something else.
}
});
}

ここでlookUpVersion()はFutureを返す関数ですが、その記述もasyncを使って簡潔に、まるで同期的なコードと同じように記述することができます。

String lookUpVersionSync() => '1.0.0';

Future<String> lookUpVersion() async => '1.0.0';

async/awaitを使った記述ではtry/catch文が簡潔になる利点があります。

async/awaitではFuture APIだけでなくStream APIもサポートしています。同期処理のfor文にawaitをつけることでブロック内部を非同期実行し、内部の処理はStream.listenで処理されます。

main() async {

...
await for (var request in requestServer) {
handleRequest(request);
}
...
}


Generators : sync* async*, yield, yield*

Dart1.9からはジェネレータをサポートしました。サポートしたのは 同期的ジェネレータ, 非同期的ジェネレータ の2つです。


sync*

sync*yieldを使うことで同期的に値を返すジェネレータを記述できます。

Iterable naturalsTo(n) sync* { 

int k = 0;
while (k < n) yield k++;
}

上記のコードは与えられたnまでの列挙を返す関数ですが、Listを作って返すのではなく遅延実行を行います。これをfor文のソースとして用いることで内部でmoveNext()が呼ばれるたびにyieldで中断した処理が再開されます。


async*

非同期のジェネレータではStream型で値を返すことが出来ます。

Stream asynchronousNaturalsTo(n) async* { 

int k = 0;
while (k < n) yield k++;
}


再帰的ジェネレータ : yield*

与えられた整数nから1までカウントダウンしていく次の再帰を使ったジェネレータは計算量が多く、無駄の多い実装です

Iterable naturalsDownFrom(n) sync* { 

if (n > 0) {
yield n;
for (int i in naturalsDownFrom(n-1)) { yield i; }
}
}

これに対して用意された文がyield*、読み方は yield-each です。上記のコードを次のように書き換えます。

Iterable naturalsDownFrom(n) sync* { 

if ( n > 0) {
yield n;
yield* naturalsDownFrom(n-1);
}
}

yield*に渡されたジェネレータが止まるまで受け取った値を呼び出し元にyieldと同じように返すことが出来ます。


周辺ツールの改善


フォーマッターの改善

dartfmtが改善され、より人間が読みやすいコードに整形するようになりました。dartfmtについてはこちらで使い方や整形のサンプルを見ることが出来ます。


Dart Analyzerの外部化 & IDE統合

Dartの分析ツール dartanalyzerがAnalysis Serverとして分離され、統合機能がIntelliJのプラグインやDart Editorに搭載されました。


コード分析の改善


  • IDEの外のプロセスで実行するようにした。

  • 未使用の変数やメンバなど、多くのヒントをくれるようになった。


コアライブラリの改善


dart:async


Future.waitにオプショナル引数cleanUp(successValue)追加

与えられたすべてのFutureが終わるまで待つFuture.waitcleanUp引数が追加されました。イメージとしてはFuture.waitに対するthenのようなものです。複数のFutureをまたいで用いられたリソースの開放などに使われることを想定しています。


新クラス:SynchronousStreamController

APIドキュメントはこちら

基本的にこれまでのStreamControllerと変わらない使い方ですが、唯一の利点として追加操作のレイテンシが改善されています。しかしながら「イベントがlistenされている途中でaddしてはいけない」など、制限事項も多くサンプルコードもないので今のところは様子見しておきたいクラスです


dart:collection


SplayTreeSetに新しいコンストラクタfrom(iterable)を追加

与えられた列挙を元にSplayTreeSetを作るコンストラクタが追加されました。


dart:convert


Utf8EncoderとUtf8Decoderのconvert関数にstartend引数を追加

オプショナル引数としてstartendが追加され、文字列を部分的にエンコード・デコードできるようになりました。


dart:core


新エラー: RangeErrorの追加

APIリファレンスはこちら

数値の範囲に関する例外を表すRangeErrorクラスが追加され、そのチェックのためのヘルパーメソッドも追加されました。


  • RangeError.checkNotNegative

  • RangeError.checkValidIndex

  • RangeError.checkValidRange

  • RangeError.checkValueInInterval


dart:io


ファイルのロックに関する変更

Dartから開いたファイルに対するロックの制御が可能になりました。file.lockfile.lockSync関数が追加されています。


HttpServerのbind,bindSecureにv6Only,shared引数の追加

bool v6Only引数はそのままIPv6だけ使うようになります。bool shared引数がtrueの時、同じプロセス上のDartサーバーにから同じアドレス・ポートへの追加のバインドが可能になる…と書かれているのですが、私にはネットワークのことがよくわからないので詳しい方がいたらコメントで教えてください。


Process.startにmode引数を追加

Process.startProcessStartMode mode引数が追加されました。開始モードの種類はNORMALDETACHEDの2つです。DETACHEDで開始されたプロセスは親(起動元)のプロセスと完全に切り離され、親プロセスが終了しても独立して動き続けます。また、stdinやstdout、stderr等を取得することも不可能になり、DETACHEDなプロセスから得られる情報はそのプロセスのpidだけです。


Process.killPidの追加

上記のDETACHEDモードに関連してpidからプロセスを殺すProcess.killPid関数が追加されました。


ServerSocketへのshared引数の追加

先述のHttpServerと同様と思われるshared引数がServerSocket.bind、RawServerSocket.bind、SecureServerSocket.bind、RawSecureServerSocket.bindにも追加されています。


SocketReferenceが非推奨に

もともとexperimentalのクラスだったので特に影響はないかと思います


SocketとRawSocketのconnectにsourceAddress引数を追加

SocketクラスとRawSocketクラスのstatic関数connectsourceAddress引数が追加されました。


StdoutにnonBlockingプロパティを追加

StdoutクラスにnonBlockingプロパティが追加されました。通常Stdoutクラスが提供するIOSinkは書き始めから書き終わりまでブロックされますが、Stdout.nonBlockingで渡されるIOSinkはブロックされません。


dart:isolate


Isolate.currentの追加

「ようやくか!」という機能ですが、そのコードが実行されているIsolateを取得できます。


Isolate APIの拡張

Isolate周りが一気に便利になりました。追加されたメソッドは以下の5つです


  • addOnExitListener

  • removeOnExitListener

  • setErrorsFatal

  • addOnErrorListener

  • removeOnErrorListener

また、spawnの第1引数のentryPointmessage引数を渡すことができるようになりました。渡される値は第2引数のmessageに与えます。

さらに、DartVMでIsolate APIを完全に実装し終えたということで、サーバーサイドDartのマルチコアでの平行化が可能となったようです。


The Isolate API has now been fully implemented in the Dart VM, making it much easier to create applications that target multiple CPUs.



まとめ

Dart1.9でECMA標準にある仕様はすべて実装を終えたということで、Dartは一旦小休止だと思います。今はGithubの方で次のECMA標準 3rd Editionの設計が進んでいるようです。それが固まり次第また次のゴールに向かってアップデートが重ねられていくと思います。

そういう意味では今こそがDartを学び始めるにふさわしいタイミングなのではないかと思います。先日DartVMをChromeに搭載しないことを決定したとのアナウンスがあり、そこそこのDart情報アンテナを張っていると自負している自分でも寝耳に水でした。Chrome上でネイティブにDartを動かすのは夢でしたがそれ以前にDartは書いていて楽しい言語です。「ブラウザでちゃんと動くJavaScriptを吐き出すことができる汎用言語」として発展していく道を選んだということはそれまで夢に費やしていた労力を現実に割り振ってくれるということだと信じていますので、今後は特にdart2jsの改善に力を入れてほしいと願っています。

ところで4月の終わりにGoogle本社で記念すべき第一回のDart Developers Summitが開催されます。( https://www.dartlang.org/events/2015/summit/ )。残念ながら現地にはいけないので中継と録画から情報をまとめたいと思っています。その一ヶ月後の5月にはGoogle I/O 2015も開催されますので、そこでの新情報にも期待しましょう。

以上。