Dartの型システムの動向(Record、extension type、value class)

# 改定

本記事投稿後にinline classがextension typeに、data classがvalue classに改名し、リリースバージョンもかなりずれ込んだので改定しました。

# サマリ

  Dart 3.0で[Recordを導入](https://medium.com/dartlang/announcing-dart-3-53f065a10635#f720 "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")したのに続き、[extension type (inline class)](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=we%E2%80%99re%20exploring%20are%20inline%20classes%20for%20wrapping%20existing%20types%20with%20zero%2Dcost%20%E2%80%9Cwrappers%E2%80%9D "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")、[value class (data class)](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=for%20enabling%20data%20classes "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")を導入する予定です。
  また、value classの最大の(?)特徴である[プライマリコンストラクタをclass全般に導入する構想](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=primary%20constructors%3B "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")もあります。
value classのもう一つの特徴である[copyWith()メソッド](https://github.com/dart-lang/language/blob/main/working/extension_structs/overview.md#copywith "language/working/extension_structs/overview.md at main · dart-lang/language")も、同じく導入予定の汎用機能である[マクロ(static meta-programming)](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=We%E2%80%99ve%20previously%20discussed%20macros%20(also%20referred%20to%20as%20meta%2Dprogramming).%20We%E2%80%99re%20in%20particular%20focused%20on%20this%20for%20enabling%20better%20deserialization%20of%20JSON%20(and%20similar)%2C%20and%20for%20enabling%20data%20classes. "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")を用いて実装するようですので、Recordにも導入されそうです。
  そうなると、Recordのextension typeでもvalue classとほぼ同等のことが出来ます。
  これらの特徴を下記の表に整理しました。

|型機能|Record|extension type|value class|extension type<br/>of Record|
|:--:|:--:|:--:|:--:|:--:|
|イミュータブル|○|○|○|○|
|バリュー型|○|−⁴|○|○⁵|
|アンボックス|○|◎⁴|○|○⁵|
|実行時型名|×|×⁴|○|×⁵|
|実行時型|○|×⁴|○|○⁵|
|静的型名|×|○|○|○|
|静的型|○|○|○|○|
|プライマリ<br/>コンストラクタ¹|○|⇨ ○|○|⇨ △⁶|
|`copyWith()`²|⇨ ○³|×|○|⇨ △⁷|
|複数フィールド|○|×|○|○|
|ステータス|導入済(3.0)|実装中(3.3?)|仕様策定中(3.4?)|3.3?|

1: value class向けに考案されたが、クラス全般に拡大適用する構想有り。(仕様策定中: 3.4?)
　先行導入されたRecordのコンストラクタと本質的に同じ。
2: マクロ(static meta-programing)(仕様策定中: 3.4以降?)を用いて導入。
3: data classにcopyWithが導入される際に、Recordに導入されない理由は見当たらない。
4: コンパイラが常にアンボックス。(他は最適化が状況に応じてアンボックス)
　従って、extension typeに実行時オブジェクトは無く、実行時型も実行時型名も無い。
　振る舞いはフィールドのそれに従う。
5: 実行時はフィールドのRecord型が露出する。
6: extension typeとRecordのプライマリコンストラクタを併用して記述可能。
7: extension typeのプライマリコンストラクタとRecordのcopyWithを併用して記述可能。

このあと、それぞれ少し詳しく説明するとともに、関連するプライマリコンストラクタ、`copyWith()`メソッド、マクロについても説明します。

# 詳細

## Record (導入済: 3.0)
Recordはアンボックス最適化を期待したイミュータブルなバリュー型です。
SwiftのTupleに相当する機能です。
`int`、`double`、`String`もリファレンス型のDartにおいて、初となるバリュー型です。
なお、全ての型がリファレンス型であるDartにおいて、バリュー型は不変のアイデンティティ(=リファレンス)を持たない(不定のアイデンティティしか持たない)ことを意味します。
詳細は[DartのRecordは多重人格 - Qiita](https://qiita.com/Cat_sushi/items/961831c30f9c54354c27 "DartのRecordは多重人格 - Qiita")を参照ください。

Recordのもう一つの特徴は独立したクラス名を持たないことで、同じ構造を持つものどうしは型チェックで弾かれませんが、これもDartでは初です。

また、関数パラメタ定義の文法を踏襲し、フィールドを含むクラス定義を兼ねたコンストラクタ定義があります。
これは、本質的には後述のプライマリコンストラクタです。

```dart
(int a, {int b, int c}) r = (1, b: 2, c: 3); // (1, 2, 3)
```

`hashCode`ゲッター、`==`オペレータ、`toString()`等も自動的に専用品でオーバライドされます。

仕様: [Records Feature Specification](https://github.com/dart-lang/language/blob/main/accepted/future-releases/records/records-feature-specification.md "language/accepted/future-releases/records/records-feature-specification.md at main · dart-lang/language · GitHub")

## extension type (実装中: 3.3?)
extension typeは既存クラスの内部表現を借りた別クラスを定義するものです。
Kotlinのvalue class (inline class)に相当するものです。
extension typeは完全に静的な型であり、唯一のstaticフィールドのラッパーオブジェクトを持たないという意味で、常にアンボックスされます。
これは、Recordと同様に実行時オーバーヘッドを抑えることを最大の目的としています。
`typedef`が単なる別名であって静的にも独立した型を持たず、IDEのサポートやコンパイラの型チェックを受けられないのに対し、extension typeにはその代替としての役割も持ちそうです。

実行時の型を持たない完全に静的なクラスは、Dartにおいて初です。

なお、`dynamic`型の変数等を介した実行時の振る舞いは剥き出しのフィールドの型のそれになります。

既に[DartPadのmaster channelで動作](https://dartpad.dartlang.org/?id=6b351e51f7d06fa7414e24c321839e32&channel=master)し、[中の人もそう言っている](https://github.com/dart-lang/sdk/issues/52013#issuecomment-1503585346)ので、Dart 3.1で導入するのはほぼ確実と思ったのですが、Dart 3.3になりそうです。

仕様: [Inline Classes](https://github.com/dart-lang/language/blob/main/accepted/future-releases/inline-classes/feature-specification.md "language/accepted/future-releases/inline-classes/feature-specification.md at main · dart-lang/language · GitHub")

## value class (Struct) (仕様策定中: 3.4以降?)
value classはアンボックス最適化を期待したイミュータブルなバリュー型です。
Kotlinのdata classに相当するものです。
DartではRecordに次いで2つ目のバリュー型になる予定です。
定義キーワードはKotlinと同じ`data class`になるか`struct`かと思ったのですが、`value class`になりそうです。
`hashCode`ゲッター、`==`オペレータ、`toString()`等も自動的に専用品でオーバライドされます。

Recordとの違いは独自のクラス名を持つことで、同じ構造をつものどうしでも型チェックで弾かれます。

仕様: [Value classes](https://github.com/dart-lang/language/blob/main/working/value-classes/feature-specification.md "language/working/value-classes/feature-specification.md at main · dart-lang/language")

### プライマリコンストラクタ (仕様策定中: 3.4?)
value classおいて実行性能もさることながら、ユーザにとって最もインパクトのあるのはプライマリコンストラクタの簡潔な構文でしょう。
プライマリコンストラクタはKotlinにおける同名の機能に相当するものです。
関数パラメタ定義の文法を踏襲し、フィールドを含むクラス定義を兼ねたコンストラクタ定義です。

```dart
value class C(int a, {int b, int c = 3});
```

これは下記と同等です。

```dart
value class C {
  C(this.a, {this.b, this.c = 3});
  int a;
  int b;
  int c;
}
```

キーワード`value class`に続く識別子はクラス名ではなく、コンストラクタ名です。
名前付きコンストラクタ形式も利用できるので、ライブラリプライベートにも出来ます。

```dart
value class C._(int a, {int b, int c = 3});
```

なお、プライマリコンストラクタは[クラス全般に拡大適用](https://github.com/dart-lang/language/issues/2364 "Primary constructor on classes · Issue #2364 · dart-lang/language")する構想が有ります。

現在は仕様策定中ですが、コンパイラにおけるフロントエンドで実装できる構文糖衣なので、実装は比較的容易で、おそらくDart 3.1で導入されると思ったのですが、Dart 3.3で導入する動きもありません。

仕様: [Fields and primary constructors](https://github.com/dart-lang/language/blob/main/working/extension_structs/overview.md#fields-and-primary-constructors "language/working/extension_structs/overview.md at main · dart-lang/language")

### `copyWith()`
value classのもう一つの特徴は自動生成される`copyWith()`メソッドでしょう。
`copyWith()`メソッドはKotlinのdata classにおける`copy()`メソッドに相当するものです。
この[`copyWith()`はマクロによって自動生成](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=We%E2%80%99ve%20previously%20discussed%20macros%20(also%20referred%20to%20as%20meta%2Dprogramming).%20We%E2%80%99re%20in%20particular%20focused%20on%20this%20for%20enabling%20better%20deserialization%20of%20JSON%20(and%20similar)%2C%20and%20for%20enabling%20data%20classes. "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")されるようです。
マクロが汎用機能であることを考えると、`copyWith()`がRecordに導入されない理由は見当たりません。

仕様: [copyWith](https://github.com/dart-lang/language/blob/main/working/extension_structs/overview.md#copywith "language/working/extension_structs/overview.md at main · dart-lang/language")

### マクロ (static meta-programming) (仕様策定中: 3.4以降?)
マクロの位置付けは、パッケージで実現していたコードジェネレータ機能の言語本体への取り込みです。
そのコードジェネレータの位置付けは、静的型付言語(Dart 2以降)におけるリフレクション機能(Dart 1以前)の代替と言えます。
`toJson()`、`fromJson()`の自動生成、クラスのイミュータブル化等が代表的な用途です。

現在仕様策定中ですが、コード生成だけあって[その影響範囲は広く、慎重な検討が続けられる](https://medium.com/dartlang/announcing-dart-3-53f065a10635#:~:text=Given%20the%20size%20and%20inherent%20risk%20in%20metaprogramming%2C%20we%E2%80%99re%20taking%20a%20very%20thorough%20approach%2C%20and%20we%20thus%20have%20no%20concrete%20timeline%20to%20share%2C%20even%20for%20finalizing%20design%20decisions. "Announcing Dart 3. 100% sound null safety. Records… | by Michael Thomsen | Dart | May, 2023 | Medium")ようですので、Dart 3.2以降の導入になるでしょう。

仕様: [Macros](https://github.com/dart-lang/language/blob/main/working/macros/feature-specification.md "language/working/macros/feature-specification.md at main · dart-lang/language · GitHub")

## Recordのextension type
独立した型機能ではありませんが、value classとの比較のために説明します。
data class同様、静的型名を持つアンボックス最適化を期待したイミュータブルなバリュー型になります。
extension typeにおけるフィールド名`f`がいかにも目障りですが、プライマリコンストラクタも比較的簡潔な記述で利用できます。

```dart
extension type C((int a, {int b, int c}) f);
var c = C((1, a: 2, b: 3));
```

仮にRecordが`copyWith()`を導入するなら、Recordのextension typesでも比較的簡潔な記述で`copyWith()`が利用できます。

```dart
var c2 = C(c.f.copyWith(b: 20));
print(c2.f) // (1, 20, 3)
```

そうなると、value classとの決定的な違いは実行時型名を持たないことだけです。
`copyWith()`が無いvalue classはいかにもインパクトに欠けるので、value classはDart 3.4以降でマクロと同時に導入されるのではないでしょうか。

もっとも、そのときにはRecordでも`copyWith()`が使えるようになっていると思いますが、Recordのvalue typeと比較してスッキリした構文がメリットになるでしょう。