1.22.0
言語仕様
破壊的変更:'Generalized tear-offs'は今回からサポートせず、エラーになります。1.21で既に言語仕様を変更し警告を追加していましたが、これがサポート完全終了の最終ステップです。なお、以前のバージョンではVMでのみのサポートでした。
-
assert()
がオプショナルの第2引数message
を採れるよう拡張されました。 (SDK issue 27342).メッセージは
assert
が失敗したときに表示されます。メッセージは任意のオブジェクトで良く、また、AssertionError.message
でこれにアクセスできます。これはよりユーザフレンドリーな例外出力に利用できます。例えば、次のアサートassert(configFile != null, "Tool config missing. Please see https://goo.gl/k8iAi for details.");
は次のような例外を出力します。
Unhandled exception: 'file:///Users/mit/tmp/tool/bin/main.dart': Failed assertion: line 9 pos 10: 'configFile != null': Tool config missing. Please see https://goo.gl/k8iAi for details. #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:33) #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:29) #2 main (file:///Users/mit/tmp/tool/bin/main.dart:9:10)
-
Null
型を型階層の「ボトム」に移動しました。これにより、Null
は全ての型のサブタイプとして認識されます。 リテラルnull
は常に全てのクラスのサブタイプのインスタンスとして扱われていましたが、今回からNull
クラスも全てのクラスのサブタイプになります。const empty = <Null>[]; String concatenate(List<String> parts) => parts.join(); int sum(List<int> numbers) => numbers.fold(0, (sum, n) => sum + n); concatenate(empty); // OK. sum(empty); // OK.
-
covariant
パラメタ修飾子を導入しました。これを指定することでメソッドのパラメタ(とそれをオーバライドするメッソドの対応するパラメタ)のオーバライド規則を緩和します。Strong Modeでは健全性のために実行時型チェックが必要となりますが、ある種のコードで有用な構造化が可能になります。次のように、あるクラスファミリーに特定できるようになります。
abstract class Predator { void chaseAndEat(covariant Prey p); } abstract class Prey {} class Mouse extends Prey {} class Seal extends Prey {} class Cat extends Predator { void chaseAndEat(Mouse m) => ... } class Orca extends Predator { void chaseAndEat(Seal s) => ... }
これは静的に型安全ではありません。というのも、次のように書けるからです。
Predator predator = new Cat(); // Upcast. predator.chaseAndEat(new Seal()); // Cats can't eat seals!
Strong Modeの健全性を保つため、共変のオーバライドを用いるメソッド(ここでは
Cat.chaseAndEat()
)のボディにコンパイラが自動的にパラメタが期待される型であることをチェックするコードを挿入します。つまり、コンパイラが下記相当を提供します。class Cat extends Predator { void chaseAndEat(o) { var m = o as Mouse; ... } }
標準仕様モード(訳注:Productionモード、Checkedモード)では、ユーザは滅多に必要としないにも関わらず、全てのパラメタでこのような不健全な振る舞いを許しています。Strong Modeは当初は全てで禁止していました。今回、この修飾子でどちらかを選べるようになりました。Strong Mode以外ではこの修飾子を無視します。
-
Strong Modeにおけるジェネリックスタイプの型引数の具体化時の般化限界規則を変更しました。型引数を省略した場合、コンパイラは何らかの型を想定する必要があります。Dart 1.0では単純に
dynamic
を想定していましたが、これでは型健全性がありません。class Abser<T extends num> { void absThis(T n) { n.abs(); } } var a = new Abser(); // Abser<dynamic>. a.absThis("not a num");
absThis()
のボディを型安全にするにはn
は少なくともnum
との仮定が必要ですが、これがT
に何らかの制約を設ける理由です。型引数としてdynamic
を想定するとこの例は破綻しますので。Strong Modeではこの般化限界を導入します。上記の例では、
num
が補完され、2行目でStringが渡される部分は正しく静的エラーとなります。とはいえ、あるケースではデフォルトの上限を見出すことが困難です。
class RuhRoh<T extends Comparable<T>> {}
初期のStrong Modeの振る舞いは意外で、意図しない結果となりました。1.22ではよりシンプルなアプローチを取り、妥当なデフォルト型引数(般化限界)が見つからない場合はエラーとなります。
コアライブラリ
-
ある型の
Future
か即時値のいずれでも動作するコードの為にFutureOr<T>
を定義しました。例えば、多くのテキスト操作(swizzler)があり、それらを連続して実行する手頃な関数(swizzle)がほしいとします。typedef String StringSwizzler(String input); String swizzle(String input, List<StringSwizzler> swizzlers) { var result = input; for (var swizzler in swizzlers) { result = swizzler(result); } return result; }
これは上手く動作します。
main() { var result = swizzle("input", [ (s) => s.toUpperCase(), (s) => () => s * 2) ]); print(result); // "INPUTINPUT". }
後に、非同期(例えばオンラインで同義語を検索するような)な操作をサポートしたいとします。
swizzle
のAPIを厳格に非同期化することも可能ですが、単純な同期型操作の戻り値をも手作業でFuture.value()
でくるむ必要が出てきます。swizzle()
関数が同期性に対してポリモーフィックであれば理想的です。つまり、同期操作と非同期操作を同時に許すということです。なお、await
は即時値でも許容されるので、これまでも動的に実装することは容易でした。Future<String> swizzle(String input, List<StringSwizzler> swizzlers) async { var result = input; for (var swizzler in swizzlers) { result = await swizzler(result); } return result; } main() async { var result = swizzle("input", [ (s) => s.toUpperCase(), (s) => new Future.delayed(new Duration(milliseconds: 40), () => s * 2) ]); print(await result); }
さて、
StringSwizzler
の型は何であるべきでしょうか。これまではdynamic
またはObject
を使う必要がありましたが、表現力を欠いていました。今回からはこのような書き方ができます。typedef FutureOr<String> StringSwizzler(String input);
名前が示す通り、
FutureOr<String>
はユニオンタイプです。String
かFuture<String>
にはなりえ、他のものにはなりえません。この例では、単によりコードの詳細な型情報を読者に提示すること以外に、特段利用価値が高いものではありません。戻り値について若干良いエラーチェックを提供しますが。FutureOr<T>
はFufure.then()
のような ジェネリック なメッソッドで本当に重要になります。このケースでは、この特別なユニオンタイプはthen()
の型引数を渡されたクロージャーを元にシステムが型推論することを助けます。以前、Strong Modeは
Future.then()
を扱うための特別なルールを実装していました。FutureOr<T>
は、第三者APIもこの恩恵に預かるための機能の一般公開でもあります。
ツールの変更
-
Dart2Js
- (長い間非推奨だった) mixin typedefを削除します。
-
Pub
- 実際にトランスフォーマを利用しない限り、実行形式のためのバーバックアセットサーバを利用する必要がなくなりました。これによりプレコンパイルは大幅に高速化し、失敗時のエラーメッセージはわかりやすくなり、グローバルに実行する実行形式からも
Isolate.resolvePackageUri()
APIを一貫して使えるようになります。 - Linuxにおいて元々設定されたパッケージのファイルのオーナーとパーミッションをパッケージ抽出時は常に無視するようになります。これは既に殆どの環境でデフォルトの動作でした。
- パケージのキャッシュから出力される構文エラーをよりエレガントに扱います。pubspecが解析できないパッケージは、今回から
pub get --offline
とpub cache repair
において無視されます。 -
pub run
で起動した子プロセスの標準入力を適正にクローズするようになりました。 -
pub serve
実行時にdart2jsから出力されるソースマップを修正しました。パッケージが提供するアセットへのURLがpub serve
が提供する場所と一致するようになります(../packages/package_name/
に代わりpackages/package_name
)。
- 実際にトランスフォーマを利用しない限り、実行形式のためのバーバックアセットサーバを利用する必要がなくなりました。これによりプレコンパイルは大幅に高速化し、失敗時のエラーメッセージはわかりやすくなり、グローバルに実行する実行形式からも
インフラ変更
- SDKはgypに代わりGNを使うようになり、ninja専用になります。ドキュメントはwikiにあります。
tools/gn.py
もヘルプも参照ください。この変更はgypの非推奨化に対応するものです。本バージョンではgclient sync
またはgclient runhooks
を実行する前に環境変数DART_USE_GYP
を設定することでgypによるビルドファイルの生成が可能ですが、これも将来は削除されます。