#note
## 環境構築
[https://dart.dev/get-dart](https://dart.dev/get-dart)
[https://dart.dev/tools/jetbrains-plugin](https://dart.dev/tools/jetbrains-plugin)
- Dart SDK path に `/opt/homebrew/opt/dart/libexec`
[https://dartpad.dev/](https://dartpad.dev/) で playground を実行できる
## チュートリアル
- [https://dart.dev/tutorials](https://dart.dev/tutorials)
- [https://dart.dev/tutorials/server/get-started](https://dart.dev/tutorials/server/get-started)
- [https://dart.dev/tutorials/server/cmdline](https://dart.dev/tutorials/server/cmdline)
- [https://dart.dev/tutorials/server/fetch-data](https://dart.dev/tutorials/server/fetch-data)
- [https://github.com/dart-lang/samples](https://github.com/dart-lang/samples) に dart が出しているサンプルがたくさんある
- [A simple Dart HTTP server](https://github.com/dart-lang/samples/tree/main/server/simple)
- [A Dart HTTP server that uses Cloud Firestore](https://github.com/dart-lang/samples/tree/main/server/google_apis)
- [Command line app sample](https://github.com/dart-lang/samples/tree/main/command_line)
- [Native compilation sample](https://github.com/dart-lang/samples/tree/main/native_app)
- サンプルの中でも言語機能を紹介する目的のもの
- [Enhanced enums samples](https://github.com/dart-lang/samples/tree/main/enhanced_enums)
- [Extension methods samples](https://github.com/dart-lang/samples/tree/main/extension_methods)
- [Null Safety](https://github.com/dart-lang/samples/tree/main/null_safety)
## 言語仕様
[https://dart.dev/language](https://dart.dev/language)
[https://dart.dev/effective-dart](https://dart.dev/effective-dart)
### Package 構成 (lib について)
[https://dart.dev/tutorials/server/fetch-data](https://dart.dev/tutorials/server/fetch-data) をリファクタリングした例
[https://dart.dev/tools/pub/create-packages](https://dart.dev/tools/pub/create-packages) にもある通り常に、 `lib/` がライブラリとなるらしい
> As you might expect, the library code lives under the lib directory and is public to other packages. You can create any hierarchy under lib, as needed. By convention, implementation code is placed under lib/src. Code under lib/src is considered private; other packages should never need to import src/.... To make APIs under lib/src public, you can export lib/src files from a file that's directly under lib.
とあるように `lib/src` は実装の詳細を記述していることが期待されている
sh
```
.
├── CHANGELOG.md
├── README.md
├── analysis_options.yaml
├── bin
│ └── main.dart
├── lib
│ ├── data.dart
│ └── http
│ ├── error.dart
│ └── http.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── fetch_test.dart
```
[https://dart.dev/tools/pub/create-packages#organizing-a-package](https://dart.dev/tools/pub/create-packages#organizing-a-package) には [shelf](https://github.com/dart-lang/shelf) の例が載っていて、テクニックとしてライブラリの利用者に対しては `lib/src` でパブリックにしていいもののみを `lib` 直下のファイルに `export` しているらしい (以下例)
dart
```
export 'src/cascade.dart' show Cascade;
export 'src/handler.dart' show Handler;
export 'src/hijack_exception.dart' show HijackException;
export 'src/middleware.dart' show Middleware, createMiddleware;
export 'src/middleware/add_chunked_encoding.dart' show addChunkedEncoding;
export 'src/middleware/logger.dart' show logRequests;
export 'src/middleware_extensions.dart' show MiddlewareExtensions;
export 'src/pipeline.dart' show Pipeline;
export 'src/request.dart' show Request;
export 'src/response.dart' show Response;
export 'src/server.dart' show Server;
export 'src/server_handler.dart' show ServerHandler;
```
### Cascade notation
カスケード記法
記法: `..` `?..`
[https://dart.dev/language/operators#cascade-notation](https://dart.dev/language/operators#cascade-notation)
dart
```
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
// カスケード記法を使った上と同一のコード
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
```
Kotlin における `apply()` みたいな気持ちっぽい
### Pub workspaces
[https://dart.dev/tools/pub/workspaces](https://dart.dev/tools/pub/workspaces)
Dart で monorepo をうまく扱えるようにするもの
また [Melos](https://github.com/invertase/melos) というサードパーティツールが古くからあり、 7系から依存管理については、 Pub workspaces に乗っかっている
[https://melos.invertase.dev/](https://melos.invertase.dev/) を見ていけばどういう機能があるかはわかるが、一番重要なのは、以下のようにスクリプトを書けることで、特に `exec` という機能については全てのパッケージに入り込んだ上でそれぞれのパッケージに同じスクリプトが実行できるという便利さがある。
ルートディレクトリで1つのスクリプトを実行したい場合には `run` という機能を使う
yaml
```yaml
melos:
scripts:
test:
# Dart テストを実行し、カバレッジレポートを生成するスクリプト
# 詳細は https://github.com/dart-lang/test/tree/master/pkgs/test を参照
run: |
# カバレッジデータを生成
dart test --coverage="./coverage"
# coverage パッケージをグローバルに有効化
dart pub global activate coverage
# カバレッジデータをフォーマットし、HTML レポートを生成
dart pub global run coverage:format_coverage --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage
genhtml -o ./coverage/report ./coverage/lcov.info
exec:
concurrency: 1
# カバレッジデータを収集し、統合レポートを生成するスクリプト
collect_coverage:
run: |
# Melos を使用して各パッケージのカバレッジファイルをリストアップ
# パッケージ名の後に /coverage/lcov.info を追加する
target_lcov_files=$(melos list --parsable | sed 's|$|/coverage/lcov.info|')
# -a オプションを使用して、複数の lcov ファイルを統合
lcov -o ./coverage/combined.lcov $(echo "$target_lcov_files" | sed 's/^/-a /')
# 統合されたカバレッジデータから HTML レポートを生成
genhtml ./coverage/combined.lcov --output-directory ./coverage/report
open ./coverage/report/index.html
```
[https://github.com/invertase/melos/issues/566](https://github.com/invertase/melos/issues/566) にもあるように `exec` はオプションを指定したい時には `run` + `exec` を書くという文法で非常にわかりづらい
### Switch と Exhaustive
[https://dart.dev/language/branches#exhaustiveness-checking](https://dart.dev/language/branches#exhaustiveness-checking)
dart
```
// nullable も網羅性の1つ
switch (nullableBool) {
case true:
print('yes');
case false:
print('no');
}
// Enum や sealed class も網羅性がある
sealed class Shape {}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r,
};
```
Switch が statement (文) なのか expression (式) で syntax が異なるらしい (不思議)
- Cases do not start with the case keyword.
- A case body is a single expression instead of a series of statements.
- Each case must have a body; there is no implicit fallthrough for empty cases.
- Case patterns are separated from their bodies using => instead of :.
- Cases are separated by , (and an optional trailing , is allowed).
- Default cases can only use _, instead of allowing both default and _.
dart
```
// statement
switch (charCode) {
case slash || star || plus || minus: // Logical-or pattern
token = operator(charCode);
case comma || semicolon: // Logical-or pattern
token = punctuation(charCode);
case >= digit0 && <= digit9: // Relational and logical-and patterns
token = number();
default:
throw FormatException('Invalid');
}
// expression (3.0 以降で出た機能らしい)
token = switch (charCode) {
slash || star || plus || minus => operator(charCode),
comma || semicolon => punctuation(charCode),
>= digit0 && <= digit9 => number(),
_ => throw FormatException('Invalid'),
};
```
### mixin
[https://dart.dev/language/mixins](https://dart.dev/language/mixins)
`with` で mix-in を利用することができる
dart
```
mixin Piloted {
int astronauts = 1;
void describeCrew() {
print('Number of astronauts: $astronauts');
}
}
// mixin を利用する
class PilotedCraft extends Spacecraft with Piloted {
// ···
}
```
`on` で mix-in が利用できるクラスを限定することができる
dart
```
class Musician {
musicianMethod() {
print('Playing music!');
}
}
mixin MusicalPerformer on Musician {
performerMethod() {
print('Performing music!');
super.musicianMethod();
}
}
class SingerDancer extends Musician with MusicalPerformer { }
main() {
SingerDancer().performerMethod();
}
```
### Implicit Interface
[https://dart.dev/language/classes#implicit-interfaces](https://dart.dev/language/classes#implicit-interfaces)
class は全て interface である ( `implicit interface` ) という考え方は特徴的な気がする
> Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B's API without inheriting B's implementation, class A should implement the B interface.
dart
```
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
String greetBob(Person person) => person.greet('Bob');
// class のままで以下のように implements も extends もできる
class Person {
final String _name;
Person(this._name);
String greet(String who) => 'Hello, $who. I am $_name.';
}
// implements (全ての元実装を実装しないとエラーとなる)
class Impostor implements Person {
String get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
// extends (一部の実装だけでもよく元の実装を使うことができる)
class Impostor extends Person {
Impostor(): super('');
String greet(String who) => super.greet(who) + '(Impostor)';
}
```
一応 explicit に interface を定義すること自体はできる (いつ使うんだろう??)
[https://dart.dev/language/class-modifiers#interface](https://dart.dev/language/class-modifiers#interface)
### Async & Await
[https://dart.dev/language/async](https://dart.dev/language/async)
### dynamic
runtime まで型の評価を延期するには `dynamic` キーワードを使う
[https://dart.dev/effective-dart/design#avoid-using-dynamic-unless-you-want-to-disable-static-checking](https://dart.dev/effective-dart/design#avoid-using-dynamic-unless-you-want-to-disable-static-checking)
### late
[https://dart.dev/language/variables#default-value](https://dart.dev/language/variables#default-value)
[https://dart.dev/language/variables#late-variables](https://dart.dev/language/variables#late-variables)
dart
```
// 基本的には、 `late` をつけなくても dart の control-flow analysis で後で設定されるている場合には問題ない
// しかし検出できない場合にはその判断を runtime に委ねる `late` をつけることでコンパイル可能になる
late String description;
void main() {
description = 'Feijoada!';
print(description);
}
```
### Metadata
`@Deprecated``@deprecated` `@override` `@pragma` のようなもの
[https://dart.dev/language/metadata](https://dart.dev/language/metadata)
`class` を普通に定義するだけでアノテーションになるのか
dart
```
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
// 利用
@Todo('Dash', 'Implement this function')
void doSomething() {
print('Do something');
}
```
### Runes
[https://dart.dev/language/built-in-types#runes-and-grapheme-clusters](https://dart.dev/language/built-in-types#runes-and-grapheme-clusters)
絵文字とかの Unicode の長さを扱えるらしい
dart
```
import 'package:characters/characters.dart';
void main() {
var hi = 'Hi 🇩🇰';
print(hi);
print('The end of the string: ${hi.substring(hi.length - 1)}');
print('The last character: ${hi.characters.last}');
}
```
output
txt
```
$ dart run bin/main.dart
Hi 🇩🇰
The end of the string: ???
The last character: 🇩🇰
```
### Function - named parameters
dart
```
// with named parameters
void main() {
doSomething(bold: true, hidden: false);
}
void doSomething({bool? bold, bool? hidden}) {
// do something
}
// without named parameters
void main() {
doSomething(true, false);
}
void doSomething(bool? bold, bool? hidden) {
// do something
}
```
> If you don't provide a default value or mark a named parameter as required, their types must be nullable as their default value will be null:
-> つまり required or default value がない場合には `bool` と宣言することができない
dart
```
void main() {
doSomething();
}
void doSomething({bool? bold, bool? hidden}) {
print(bold); // null
print(hidden); // null
}
// required + default value
void main() {
doSomething(bold: true);
}
void doSomething({required bool? bold, bool? hidden = false}) {
print(bold);
print(hidden);
}
```
### Construcotors
コンストラクタと呼ばれるものがたくさんある
- Generative constructors
- Named constructors
- Redirecting constructors
- 特殊系
- Default constructors
- Constant constructors
- Factory constructors
- Redirecting factory constructors
また以下の順番で実行される
1. initializer list
2. superclass's unnamed, no-arg constructor
3. main class's no-arg constructor
dart
```
class Person {
String? firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// `super.fromJson(data)` を呼ばない場合には
// Error: The superclass, 'Person', has no unnamed constructor that takes no arguments.
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
void main() {
var employee = Employee.fromJson({});
print(employee);
// Prints:
// in Person
// in Employee
// Instance of 'Employee'
}
```
Generative constructors, Named constructors, Redirecting constructors
dart
```
class Point {
static const Point origin = Point(0, 0);
double x;
double y;
// Generative constructors
Point(this.x, this.y);
// Named constructors
Point.origin() : x = 0, y = 0;
// Redirecting constructors
Point.alongXAxis(double x) : this(x, 0);
}
```
Default constructors
> If you don't declare a constructor, Dart uses the default constructor. The default constructor is a generative constructor without arguments or name.
Constant Constructor
dart
```
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
```
Factory constructors
インスタンスがすでにある場合にキャッシュから返すなどする場合には `factory` を利用する
考え方としてはインスタンス化を含む部分をユーザーに委ねる機能という感じ?
dart
```
class Logger {
final String name;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache = <String, Logger>{};
Logger._internal(this.name);
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
}
```
Redirecting factory constructors
別のクラスにインスタンス化も含めて委譲するケースで利用
dart
```
factory Listenable.merge(List<Listenable> listenables) = _MergingListenable
```
ちなみに Constuctor のあとの `:` は `initializer list` と呼ぶらしい
> Before the constructor body runs, you can initialize instance variables. Separate initializers with commas.
[https://dart.dev/language/constructors#use-an-initializer-list](https://dart.dev/language/constructors#use-an-initializer-list)
多分 Constructor の処理の一環として実行してくれるみたいな機能っぽい
dart
```
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}
```
### Extension Methods
dart
```
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
// 利用
print('42'.parseInt()); // Use an extension method.
// 無名の extension も作ることができるが、名前解決でコンフリクトした時には解決すべき方法が限られる
extension on String {
bool get isBlank => trim().isEmpty;
}
```
### Extension types
> An extension type is a compile-time abstraction that "wraps" an existing type with a different, static-only interface.
とあるのでおそらく `inline class` のような形でコンパイルタイムで解決される型な気がする
### Callable objects
`call()` を実装していると関数として実行することが可能らしい
dart
```
class WannabeFunction {
String call(String a, String b, String c) => '$a $b $c!';
}
var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');
void main() => print(out);
```
dart
```
extension type IdNumber(int id) {
// Wraps the 'int' type's '<' operator:
operator <(IdNumber other) => id < other.id;
// Doesn't declare the '+' operator, for example,
// because addition does not make sense for ID numbers.
}
```
# 言語進化
[https://dart.dev/resources/language/evolution](https://dart.dev/resources/language/evolution)
## Dart 3.0
特に Dart 3.0 には破壊的な変更が多く入っている
[https://dart.dev/resources/dart-3-migration](https://dart.dev/resources/dart-3-migration)
[Records](https://dart.dev/language/records)
dart
```
var record = ('first', a: 2, b: true, 'last');
print(record.$1); // 'first'
print(record.a); // 2
print(record.b); // true
print(record.$2); // 'last'
```
のように複数の値を1つの変数の中に即席オブジェクトのように格納できる
record の等価性は、あくまでも同じ shape かどうか
dart
```
(int x, int y, int z) point = (1, 2, 3);
(int r, int g, int b) color = (1, 2, 3);
print(point == color); // 'true'
({int x, int y, int z}) point = (x: 1, y: 2, z: 3);
({int r, int g, int b}) color = (r: 1, g: 2, b: 3);
print(point == color); // 'false'
```
[Patterns](https://dart.dev/language/patterns)
Q. どこまでが 3.0 以降の機能なのかわかってない
> In general, a pattern may match a value, destructure a value, or both, depending on the context and shape of the pattern.
[https://dart.dev/language/pattern-types](https://dart.dev/language/pattern-types) を見る限り、
`subpattern1 || subpattern2` や `subpattern1 && subpattern2` から `var bar, String str, final int _` というようなものまで Pattern としているっぽい
もちろん Map や Record なども Pattern
[if-case](https://dart.dev/language/branches#if-case)
dart
```
if (pair case [int x, int y]) {
print('Was coordinate array $x,$y');
} else {
throw FormatException('Invalid coordinates.');
}
```
[Guard clause](https://dart.dev/language/branches#guard-clause)
dart
```
void main() {
var pair = (2,1);
switch (pair) {
case (int a, int b) when a > b:
print('a is greater than b');
}
}
```