Kotlin 1.4の計画および将来的な展望

KotlinConf 2019の基調講演で、弊社でKotlin開発をリードするAndreyは現在Kotlinを進化させるにあたって重点を置いている分野に関する戦略的展望と、2020年にリリースされるKotlin 1.4の計画について話しました。

基調講演全体を見る(英):

私たちのビジョンは、Kotlinが皆様のあらゆる努力を支える頼もしいパートナーとなり、皆様の業務に使用されるデフォルトの言語になることです。 このビジョンを達成するため、すべてのプラットフォームでKotlinにさらなる進化をもたらす予定です。 世界中の業界を代表する企業におけるいくつものケーススタディから、私たちがこの方向性において順調に前進していることがわかります。

2020年春に登場予定のKotlin 1.4により、Kotlinエコシステムの実現に向けてさらに一歩前進することになります。

品質への注力

Kotlin 1.4では品質とパフォーマンスを第一に注力する予定です。 Kotlinはすでに多くのアイデアと手法を取り入れているモダンなプログラミング言語です。 私たちはKotlinを常に最新の状態に保ち、絶えず進化させていく予定です。 ただし、現時点のKotlinは、大きな機能を追加するよりも全体的なプログラミング体験を改善することが重要な段階に達していると考えています。 そのため、Kotlin 1.4では以下で詳細に記載するいくつかの小さな言語上の変更を行うに留めています。

一方で、私たちはKotlinに対応したIDEのパフォーマンス向上において、すでに目覚ましい結果を残しています。 コード補完速度が旧バージョンと比較して大幅に向上しました。

加えて、Gradleチームと協力し、Gradleスクリプトを高速化しました。 Kotlin 1.3.60はKotlin 1.3.10と比較して2.5倍高速化しており、メモリ消費量が75%少なくなっています。

メモリ

さらに、build.gradle.ktsの読み込む際のCPU使用量はほぼゼロとなっています! また、コードキャッシュにより、開発モードでのKotlin/Nativeのコンパイル処理速度が最大2倍になっています。

私たちはビルド速度がユーザーにとって最大の懸念事項であることを理解しており、この問題を解決するためにツールチェーンの改善を継続的に行っています。 しかし、少しずつ改善を行っても実際のコードベースの自然増加には追い付けません。私たちがコンパイル速度を向上させても、ユーザーがより多くのコードを書けば、全体的なビルド時間は十分に改善されません。 コンパイラを再実装して大幅に高速化する必要があることは明確です。

新しいコンパイラ

新しいコンパイラの実装目標は、大幅な高速化とKotlinがサポートするすべてのプラットフォームの統合、ならびにコンパイラ拡張機能のAPIを提供することです。 この目標を実現するには数年を要する見込みですが、私たちはすでに少し前からこの取り組みを開始しているため、この新しい実装の一部は1.4で提供される予定です。また、移行は非常に段階的に実施されます。 移行はすでに始まっています。例えば、型推論の新しいアルゴリズムが新しいコンパイラに組み込まれています。 その他の部分に対するアプローチも同じです。すなわち、古いバージョンに加えて新しいバージョンを実験的モードで同時に提供し、新しいバージョンが安定した時点でそちらをデフォルトにする予定です。

新しいフロントエンドによる高速化

新しいコンパイラの大部分は、新しいフロントエンドを実装することで高速化される予定です。

少し背景を説明しますと、コンパイルはソースファイルを取得してそれを段階的に実行可能なコードに変換するパイプラインであると考えることができます。 このパイプラインで最初の大きなステップ(処理)は、コンパイラのフロントエンドと呼ばれています。 このステップではコードを解析して名前を解決したり、型チェックを実行したりします。 コンパイラのこの機能は、エラーをハイライト表示する際や定義に移動する際、ならびにプロジェクト内でシンボルが使用されている箇所を検索する際にも動作します。 また、これは現時点でkotlincが最も時間を要しているステップであるため、その高速化を図りたいと考えています。

現在の実装は未完成であり、1.4での提供は予定しておりません。しかし、時間のかかる作業のほとんどはすでに行われているため、どの程度高速化されるかを見積もることはできます。 私たちのベンチマーク(YouTrackとKotlinコンパイラ自身のコンパイル)によれば、新しいフロントエンドは既存フロントエンドの約4.5倍高速化されることが分かっています。

統合されたバックエンドと拡張性

フロントエンドがコード解析を完了すると、バックエンドが実行可能ファイルを生成します。 バックエンドには、Kotlin/JVM、Kotlin/JS、Kotlin/Nativeの3種類があります。 最初の2つはこれまで別々に開発されてきたため、多くのコードは共有していませんでした。 一方、Kotlin/Nativeの開発を始めるにあたって、私たちは、仮想マシンにおけるバイトコードのような、Kotlinコードの内部表現(IR)をベースとする新しいインフラストラクチャを開発しました。 現在、他の2つのバックエンドを(Kotlin/Nativeで採用された)IRに統合しているところです。 結果的に多数のバックエンドロジックが共有され、パイプラインが統合されることになるため、すべてのターゲットに対してほとんどの機能実装、最適化、バグ修正をわずか1回で実行できるようになります。

私たちは新しいバックエンドへの移行を段階的に進めています。1.4の時点では新しいバックエンドがデフォルトで有効化される可能性は低いですが、ユーザーが使用することを明示的に選択できるようにする予定です。

バックエンドのインフラストラクチャを共通化することで、マルチプラットフォームに対応したコンパイラの機能拡張を行えるようになります。 パイプラインに組み込み、すべてのターゲットに対して自動的に機能するようなカスタム処理や変換処理を追加できるようになります。 1.4ではそのような機能拡張用の公開API(このAPIは今後stable化する予定です)は提供されませんが、私たちは現時点ですでにコンパイラプラグインを構築しているJetPack Composeなどのパートナーと緊密に連携しています。

Kotlinライブラリフォーマット(KLib)の導入

Kotlinでマルチプラットフォームライブラリを作り、それを配布してクライアントに使ってもらうためには、どのプラットフォームでも等しく動作する配布フォーマットが必要になります。 このため、Kotlinマルチプラットフォーム用のライブラリフォーマットであるKLibを導入します。 KLibファイルにはシリアル化されたIRが含まれています。 このライブラリは依存関係としてあなたのコードに追加することができます。コンパイラのバックエンドはこれを検出し、任意のプラットフォームで実行可能なコードを生成します。 また、バイトコードとの類似性も確保されており、JVMバイトコードとほぼ同じようにKLibsを解析・変換できます。 シリアル化されたIRに行われるすべての変換は、KLibが使用されるすべてのプラットフォームに影響します。

実際、Kotlin/Nativeは長い間KLibs形式を使用してKotlinネイティブのライブラリを配布してきましたが、現在は他のバックエンドやマルチプラットフォームライブラリに対応するために形式を拡張しています。 この形式は1.4では実験的なものになりますが、今後のバージョンでは安定版のABIを提供できるようにする予定です。

マルチプラットフォームに関するその他のお知らせ

Android Studio内でのiOSコードの実行

現在、iOSのデバイスやシミュレーターでKotlinコードの実行、テスト、デバッグを実現するAndroid Studio用のプラグインを開発しています。 このプラグインはIntelliJ独自のコードを使用しているため、ソースは非公開となります。 また、Objective-C言語やSwift言語には対応せず、AppStoreへの配布を含む一部の操作ではXcodeを実行する必要があります。ただし、Kotlinコードで行えることなら何でも新しいプラグインをインストールしたAndroid Studioからは実行できるようになります。 このプラグインのプレビューは、2020年に公開予定です。

Kotlin/Nativeランタイムの改善

Linux、Windows、macOS、iOSのほか、Kotlin/NativeがwatchOSとtvOSで動作するようになりました。これにより、事実上どのデバイスでもKotlinを実行できるようになりました。 また、iOSでKotlinプログラムをさらに高速に実行できるようにするため、Kotlin/Nativeランタイムのパフォーマンス向上にも取り組んでいます。

コアライブラリ

Kotlinのコアライブラリはすべてのプラットフォームで動作します。 コアライブラリには、あらゆる基本型とコレクションを扱うkotlin-stdlibkotlinx.coroutineskotlinx.serialization、およびkotlinx.ioが含まれています。 マルチプラットフォームの世界では日付への対応が切に求められており、私たちはまさにその対応を行っているところです。実験的なDurationsはすでにstdlibに追加されており、DateTimeへの対応は現在進行中です。

Kotlinライブラリに追加されたその他の重要な機能には、Reactive Streamsのコルーチンベースの実装であるFlowがあります。 Flowはデータストリームの処理を得意としており、データストリームを処理する際にKotlinの能力を利用しています。 Flowは人間工学的な側面だけではなく、動作速度的にも優れています。 いくつかのベンチマークでは、既存の一般的なReactive Streamsの実装に比べて約2倍の速度を記録しています。

ライブラリ作者向けの改善

Kotlinエコシステムにとって、新しいライブラリの作成は非常に重要であると考えており、私たちはライブラリ作者のプログラミング体験をより快適にするための改善を継続的に行っています。 新しいライブラリ作成モードは、安定したAPIに最適な方法でコードを成形するのに役立ちます。 また、すべてのプラットフォームでドキュメントを生成できるよう、Dokka 1.0のリリースを予定しています。

マルチプラットフォームウェブ

複数のプラットフォームでコードを共有することはモバイル端末だけではなく、ウェブクライアントにとっても素晴らしいことです。それによって、多くのコードをサーバーや各種モバイルアプリで共有できるからです。 私たちはKotlin/JS関連ツールへのより一層の投資を進めており、Kotlinコードの変更からブラウザでの結果表示までの一連の開発サイクルを非常に高速に実行できるようになりました。

また、JSとの相互運用性も改善され、NPMの依存関係をKotlinプロジェクトに添付できるようになります。加えて、.d.ts型定義はKotlinのツールチェーンによって自動的にピックアップされるようになります。

新しいIRベースのバックエンドでは、バイナリのサイズも大幅に改善されています。 コンパイルされたJSファイルのサイズは、現在の半分になる可能性があります。

新しい言語機能

Kotlin 1.4にはいくつかの新しい言語機能が実装される予定です。

KotlinクラスのSAM変換

KotlinクラスのSAM変換サポートは、コミュニティからの強い要望でした(KT-7770)。 SAM変換は、単一の抽象メソッドのみを持つインターフェースやクラスがパラメータとして期待される場合にラムダ式を引数として渡す場合に適用されます。 その後、コンパイラはラムダ式を抽象メンバー関数を実装するクラスのインスタンスに自動変換します。

現在、SAM変換はJavaのインターフェースと抽象クラスにのみ機能します。 この設計の元々のアイデアは、このようなユースケースに対して、関数型を明示的に使用するというものでした。 しかし、関数型や型エイリアスではすべてのユースケースに対応できず、往々にしてSAM変換を行うためだけにJavaインターフェースを維持しなければならないことが分かりました。

Javaとは異なり、Kotlinは単独の抽象メソッドを持つすべてのインターフェースに対するSAM変換を許可しません。 また、私たちはSAM変換に適したインターフェースを作成する意図は明確であるべきだと考えています。 したがって、SAMインターフェースを定義するには、funキーワードでインターフェースをマークし、それが関数型インターフェースとして使用できることを強調する必要があります。

fun interfaceの代わりにラムダ式を渡せるのは、新しい型推論アルゴリズム使用時のみであるということに留意してください。

名前付き引数とポジショナル引数の混在

Kotlinでは、明示的な名前を持つ引数(「名前付き引数」)と名前を持たない通常の引数(「ポジショナル引数」)を混在させることを禁止しています(ただし、すべてのポジショナル引数の後に名前付き引数を配置する場合は除きます)。 しかし、すべての引数が正しい位置にある状態で、途中にある引数の名前を指定したい場合には非常に煩わしいものです。 Kotlin 1.4ではこの問題を解消し、以下のようなコードを書けるようになります。

デリゲート型プロパティの最適化

lazyプロパティとその他いくつかのデリゲート型プロパティの基本的なコンパイル方法を改善する予定です。

一般的にデリゲート型プロパティは、対応するKPropertyリフレクションオブジェクトにアクセスできます。 例えばDelegates.observableを使用する場合、次のように変更されたプロパティに関する情報を表示できます。

これを実現可能にするため、Kotlinコンパイラは補足的な構文上のメンバープロパティを生成し、配列にクラス内で使用されるデリゲート型プロパティを表す全KPropertyオブジェクトを保存します。

ただし、一部のデリゲート型プロパティはいかなる場合もKPropertyを使用しません。 それらの場合は、$$delegatedPropertiesでオブジェクトを生成するのは最適ではありません。 Kotlin 1.4のリリースでは、このような場合も最適化します。 デリゲート型プロパティの演算子がinlineであり、KPropertyパラメータが使用されていない場合、対応するリフレクションオブジェクトは生成されません。

その最も顕著な例が、lazyプロパティです。 lazyプロパティのgetValueの実装はinlineであり、KPropertyパラメータを使用しません。

Kotlin 1.4以降、lazyプロパティを定義する際には対応するKPropertyのインスタンスは生成されません。 クラス内で使用している唯一のデリゲート型プロパティがlazyプロパティ(およびこの最適化に適合するその他のプロパティ)である場合、該当クラスには$$delegatedProperties配列全体が生成されません。

末尾のカンマ

この小さな構文の変更は素晴らしく便利です! パラメータリストの最後のパラメータの後に末尾のカンマを追加できます。 その後、行を入れ替えたり不足しているカンマを追加・削除したりすることなく新しいパラメータを追加できます。

その他の注目すべき変更点

Kotlin 1.3.40で導入された便利なtypeof関数がstable化し、すべてのプラットフォームでサポートされるようになります。

whenの内側でbreakcontinueを有効化できる機能については、すでにリリース1.3.60のブログ投稿で取り上げました。

おわりに

Kotlin EAPと実験的機能を試していただき、フィードバックを提供いただいた皆様に心から感謝いたします。 私たちは皆様と一緒にKotlin言語を開発し、皆様からの貴重なご意見に基づいて多くの設計上の決定を下しています。 コミュニティでこの迅速かつ効果的なフィードバックのサイクルを維持することは、Kotlinを可能な限り最高の言語にするために非常に重要です!

Kotlinを使って非常に多くの素晴らしい作品を作成していただいたコミュニティメンバーの皆様に心から感謝いたします。 共にKotlinで開発し続けましょう!

最後に、IntelliJ IDEAとAndroid Studio内のKotlinプラグインは、機能の利用に関する匿名の統計情報を収集する機能を有しています。 この統計情報は正常に動作している機能や、問題を引き起こしている機能、ならびに集中的に改善すべき機能を把握するのに役立つため、統計情報の提供にご協力いただけますようお願い申し上げます。

[原文Original post in English is written by Svetlana Isakova

image description