最初に言っておきます。
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
async
とawait
は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.wait
にcleanUp
引数が追加されました。イメージとしてはFuture.wait
に対するthen
のようなものです。複数のFutureをまたいで用いられたリソースの開放などに使われることを想定しています。
新クラス:SynchronousStreamController
APIドキュメントはこちら
基本的にこれまでのStreamControllerと変わらない使い方ですが、唯一の利点として追加操作のレイテンシが改善されています。しかしながら「イベントがlistenされている途中でaddしてはいけない」など、制限事項も多くサンプルコードもないので今のところは様子見しておきたいクラスです
dart:collection
SplayTreeSetに新しいコンストラクタfrom(iterable)
を追加
与えられた列挙を元にSplayTreeSetを作るコンストラクタが追加されました。
dart:convert
Utf8EncoderとUtf8Decoderのconvert
関数にstart
とend
引数を追加
オプショナル引数としてstart
とend
が追加され、文字列を部分的にエンコード・デコードできるようになりました。
dart:core
新エラー: RangeErrorの追加
APIリファレンスはこちら
数値の範囲に関する例外を表すRangeErrorクラスが追加され、そのチェックのためのヘルパーメソッドも追加されました。
- RangeError.checkNotNegative
- RangeError.checkValidIndex
- RangeError.checkValidRange
- RangeError.checkValueInInterval
dart:io
ファイルのロックに関する変更
Dartから開いたファイルに対するロックの制御が可能になりました。file.lock
やfile.lockSync
関数が追加されています。
HttpServerのbind,bindSecureにv6Only,shared引数の追加
bool v6Only
引数はそのままIPv6だけ使うようになります。bool shared
引数がtrueの時、同じプロセス上のDartサーバーにから同じアドレス・ポートへの追加のバインドが可能になる…と書かれているのですが、私にはネットワークのことがよくわからないので詳しい方がいたらコメントで教えてください。
Process.startにmode引数を追加
Process.start
にProcessStartMode mode
引数が追加されました。開始モードの種類はNORMAL
とDETACHED
の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関数connect
にsourceAddress
引数が追加されました。
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引数のentryPoint
にmessage
引数を渡すことができるようになりました。渡される値は第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も開催されますので、そこでの新情報にも期待しましょう。
以上。