Dartの基本文法を振り返る

# Dart
DartはGoogleによって開発されたプログラミング言語です。
もともと、JavaScriptを置き換える言語を目指していましたが、Googleがとりあえず諦めてしまったようなので、今はAltJSの選択肢の一つになっています。

なぜ、今Dartなのかというと

+ 最近Dartを使っているFlutterが流行っているから
  + FlutterでWebアプリが作れる
  + FlutterのWindows版が正式版として使える
  + Flutterの他のPC版(macOS, Linux)がbetaで動くようになってきてる
+ 個人的にDartが好きだから

## Dartの言語仕様
Dartはコンパクトな言語仕様らしく仕様書は目次などを含め150ページほどです。(C++17のドラフトは1500ページ弱あります。)

基本形はJavaで、それにJavaScriptの考え方の一部を取り入れた感じです。
影響を受けた言語(from Wikipedia)ではJavaScript, Java, Smalltalk, Erlang, Strongtalk, C#となっていました。
このJava風の言語仕様はすでにWebでJavaScriptを書いてきた人向けというよりは、JavaやSwiftでアプリを書いてたりC++を使って何か書いていたりみたいな人向けなのかもしれません。

DartはネイティブコンパイルやJavaScriptへのトランスパイルができ、DartVMというVM上で動かすこともできます。
コンパイルには`dart compile`というコマンドを使います。
このコマンドは`exe`と`aot-snapshot`、`jit-snapshot`、`kernel`、`js`をサブコマンドとして取ります。
ネイティブコンパイルする場合は`exe`を使用します．このサブコマンドではプラットフォームに依存した実行ファイルを作成します。これには記述したDartのプログラムとDartのランタイムが含まれます。
`aot-snapshot`はDartのプログラムをプラットフォームに依存するバイナリを作成しますがDartのランタイムは含まれません。これで作成されたプログラムは`dartaotruntime`コマンドを使い実行します。
`jit-snapshot`はDartプログラムを中間表現にコンパイルします。実行には`dart run`コマンドを使用します。これで作成されたプログラムはプラットフォームに依存します。
`kernel`は中間表現 (カーネルAST) にコンパイルします。これはプラットフォームに***依存しない***形式です。実行には`dart run`コマンドを使用します。
`js`はJavaScript`にコンパイルします。

```shell
dart compile exe bin/main.dart
dart compile aot-snapshot bin/main.dart
dart compile jit-snapshot bin/main.dart
dart compile kernel bin/main.dart
dart compile js bin/main.dart
```

## 環境
Dartの開発にはいくつかの選択肢がありますが、IntelliJ IDEAを使用するのが一番簡単だと思います。

## ここからの流れ
+ 型
+ 変数
+ 関数
+ クラス
+ パッケージ
+ 非同期

の順で説明していきたいと思います。

また、null safetyになってから結構経つので、null safetyであること前提で記述している箇所もありますので、null safety以前のDartをお使いの場合は気を付けてください。

# 型
## 基本型
+ 数値型は`int`と`double`(それ以外にも数値を扱う型はあります)
+ 文字列は`String`型
+ リストには`List<T>`型
+ 連想配列には`Map<K, V>`型

## 型の表記
Dartの型の表記には`dynamic`と`var`を使うこともできます。
`dynamic`は動的型で`List<dynamic>`などのように書きたい時にも使えます。
`var`は型推論を行い代入時の型になります。なので、再代入時に別の型の値を入れるとエラーになります。

動的型を乱用することは良いとは思いませんが、動的型を使わないと記述不可能な場面は出てくると思いますので、ここぞという時に使うと良いと思います。

## 型のTips
Dartの型の多くは`Object`型からの派生です。つまり、`int`も`double`も`String`も全て`Object`から派生しています。
しかし、例外として`null`の型である`Null`型はObjectから派生していません。
そのため、次のような実行結果になります。

```dart
null is Object; // false

int? i1;
i1 is Object; // false

i1 = 0;
i1 is Object; // true

int i2 = 0;
i2 is Object; // true
```

また、Dartでは暗黙の型変換ができません。例えば一般的には普通にできる`int => double`の変換はそのまま代入では不可能ということです。なので`toInt()`のようなメンバ関数が用意されています。

## null safety
Dart 2.12から`null`を許容する型と許容しない型を明示することができるようになりました。
KotlinやSwiftにも同等の機能があります。
この機能によってモダンな感じでコードを書くことができます。

```dart
int? // nullable
int  // not null
```

上記の例のように`型名?`のような型が`null`になりうる型で、`?`が付かない型が`null`を許容しない型です。
ここからは`null`になりうる型をnull許容型、そうでない方を非null型と呼びます。 (勝手にそう呼ぶだけです)

null許容型は`null`が入っているかもしれない型なので、そのまま値を使おうとするとエラーになります。
例えば以下のような場合はエラーになります。

```dart
int? a = null;

a.toString(); // aはnullかもしれない
```

そのため、null許容型を非null型にしなければなりません。

```dart
int? a = null;

if (a != null) {
  a.toString(); // aはnullかもしれない
}
```

この場合`a.toString()`をする前につまりnull許容型の値を使う前に`null`チェックをしています。
これにより`if`文の中では`a`が`null`でないと言えるので普通に`a.toString()`を使うことができます。

また、これは昔からある機能ですが、`?.`や`??`によって、値が`null`か`null`でないかで処理を分けることができます。

```dart
int? a = null;

a?.toString(); // a == nullであればnull、a != nullであればa.toString()の値になる
a ?? 0; // a == nullであれば0、a != nullであればaの値になる
```

# 変数
Dartの変数は型をJavaの書式とほぼ同じです。

```dart
int number = 0;
String str = 'Dart';
var one = 1;
```

## finalとconst
Dartは型の修飾子として`final`と`const`が存在しています。
`final`はJavaの`final`と同じ意味です。つまり再代入不可であることを示します。
`const`はコンパイル時定数。つまり、「再代入不可 + そのメモリ領域の書き換え不可」を表します。

```dart
final int zero = 0;
final one = 1; // 型推論もできる

const int two = 2;
const three = 3; // こっちでも型推論できる
```

## 不可視な識別子
Dartはそのファイル内など見える範囲を制限することができます。
ある識別子(変数とか関数とか)を他のソースで見えないようにするには`_`で名前を始めます。

```dart
int visible = 0;
int _invisible = 0;
```

なので、Dartには`public`とか`protected`とか`private`とかの予約語がありません。

# 関数
Dartはもちろん関数があります。
Javaとは違い関数はクラスに属する必要がありません。

JavaやC++, C#などの言語からDartを始めた人が割と戸惑う点としてDartは関数のオーバーロードをサポートしていません。
なので、全ての関数は別の名前がつけられている必要があります。

関数は次のように書きます。

```dart
int func(int a, int b) {
  return a + b;
}
```

完全にC系の言語の書き方です。
Dartではさらに返り値の型を省略することもできます。

```dart
func(int a, int b) {
  return a + b;
}
```

また、引数の型も省略できます。


```dart
func(a, int b) {
  return a + b;
}
```

## `sync*`
Dartは関数の後ろに`sync*`をつけることでPythonで言うジェネレータを定義することができます。
ここで出てくる`sync`というのは同期 (synchronous) という意味で、後に説明する非同期に対して同期と言っています。

```dart
// [0, max)の整数列を返すiota関数
Iterable<int> iota(int max) sync* {
  for(int i = 0; i < max; ++i) {
    yield i;
  }
}

// main
//  実行結果
//   0
//   1
//   2
//   3
//   4
void main() {
  for(final n in iota(5)) {
    print(n);
  }
}
```

このコード例の`yield`は「生む」や「譲る」という意味があります。
`yield`を行うと一つ値を「生む」処理を行います。これを`for`文で実行した場合は次のような動作になります。
1. `yield`が来た時点で呼び出し元のに実行権を「譲る」処理を行います。
1. 呼び出し元の`for`で1回ループが周ります。
1. ループが回った後にもう一度`yield`の続きからジェネレータの処理が継続されます。

この処理がジェネレータ関数を抜けるまで繰り返されます。

## メソッド
Dartはクラスに属する関数を作ることができます。
一般的には非メンバ関数と同じ書き方ですが、暗黙的に自インスタンスへの参照として`this`が渡されます。

### コンストラクタ
クラスを構築する特殊な関数としてコンストラクタがあります。
コンストラクタにはいくつかの書き方が存在しています。

#### 普通のコンストラクタ
一般的なクラスベースのオブジェクト指向言語と同じくDartの普通のコンストラクタはクラス名と同一の名前をもち返り値の型を書きません。

```dart
class Klass {
  Klass();
}
```

#### 名前付きコンストラクタ
Dartは関数のオーバーロードができませんが、この制約はコンストラクタにも付きます。
なので普通のコンストラクタを作ってしまうと、別の引数をとる普通のコンストラクタを作ることができなくなります。
そこで、Dartにはコンストラクタを名前付きで作ることができます。

```dart
class Klass {
  Klass.fromString(String f);
}
```

もうお分かりだと思いますが、名前付きコンストラクタも同名では作ることはできません。

#### ファクトリ
`factory`をコンストラクタに指定すると、そのコンストラクタをファクトリとして指定することができます。
ファクトリは一般的なコンストラクタとは違いインスタンスを作成し返します。

インスタンス生成に引数を作るなどの前処理が必要であったり、インスタンス生成後にしかできない処理をする際に便利です。

```dart
class Klass {
  Klass._private(); // 非公開な名前付きコンストラクタ
  factory Klass() {
    var instance = Klass._private(); // newは省略できる
    return instance;
  }
}
```

#### 定数コンストラクタ
定数オブジェクトを作成することができるコンストラクタです。
メンバ変数は全て`final`で修飾し、コンストラクタ名の前に`const`をつけます。

```dart
class Klass {
  final String name;
  const Klass(this.name); // this.nameのようにするとnameという引数で受けそのままthis.nameに代入できる
}
```

## 特殊な引数
Dartは名前付き引数とオプションの引数をサポートしています。

### 名前付き引数
名前付き引数は引数の名前を指定して値を渡す引数です。
名前付き引数を使うことで、次のような利点があります。

+ 仮引数名が適切であれば読みやすいコードになる
+ 引数の順番にこだわる必要がない

名前付き引数は引数を`{}`で括ります。

```dart
func({first, last});

// 呼び方
func(first: 'f'); // firstだけ指定
func(last: 'l'); // lastだけ指定
func(first: 'f', last: 'l'); // firstとlastを指定
```

この`first`と`last`が名前付き引数となります。

名前付き引数にした引数は引数が指定されないということもできてしまいます。
指定されなかった場合はデフォルトで`null`になります。
しかし、名前付き引数にはしたいけど、絶対引数は渡してほしいということはあると思います。
この両方を満たすために使われるのが`@required`です。

```dart
func({first, @required last});
```

`@required`は`meta`というライブラリに含まれているようで標準ではありませんが、Flutterでは使えるので知っておくと便利だと思います。

Dart 2.12からはnull safetyが標準となり、引数を渡さないと`null`になるという仕様と`@required`がバッティングしてしまいます。
Dart 2.12からは`@required`ではなく`required`というキーワードを使い必ず渡す必要があることを明示できます。

```dart
func({first, required last});
```

### オプションの引数
オプションの引数は渡さなくても良い引数のことです。

```dart
func([first, last]);
```

### デフォルトの引数
名前付き引数やオプションの引数で渡されなかった引数には`null`が入ります。
これを`null`以外の値にしたいときにデフォルトの引数を使用します。

```dart
named({first, last='l'});
opt([first, last='l']);
```

この例では両方ともlastに引数が指定されなかった場合に`'l'`が使用されます。

# クラス
DartはJavaScriptとは異なりクラスベースのオブジェクト指向をサポートしています。

クラスの書き方はJavaに似ています。

```dart
class Klass {
  String _name;

  String getName() {
    return _name;
  }
}
```

これはDartのコードですがJavaのコードにも見えます。
前述の通りDartはアクセス修飾を変数の名前で示すため、`private`や`public`などの単語を書きません。
この例では`_name`がprivateのようなもので、`getName`がpublicとなります。

## プロパティ
最近の言語はプロパティという仕組みを持っています。
プロパティはゲッターとセッターを扱いやすくしたものです。

```dart
class Klass {
  String _name; // 外部からは取得だけ許可したい

  String get name => _name;
}
```

この例の`name`がプロパティでゲッターです。

セッターは次のように作ります。

```dart
class Klass {
  String _name;

  String get name => _name;
  set name(String v) {
    _name = v;
  }
}
```

# パッケージ
Dartにはパッケージという概念があります。
パッケージはライブラリのようなものでいくつかの機能などをひとまとめにしたものです。

パッケージの作り方はここでは省略し、使い方を紹介します。

## `import`
Dartはパッケージを`import`という単語を使用し読み込みます。

```dart
import 'dart:math';
```

この例ではDartの標準ライブラリのなかの`math`を読み込んでいます。

### `as`
単に`import`をしただけでは中の識別子が名前空間もなく使えるようになります。
しかし、名前の衝突を避けるために名前空間のようなものを指定したいことがあります。
ここで使うのが`as`です。

```dart
import 'dart:math' as m;
```

とすることで`dart:math`内の識別子が`m.???`の形式で使えるようになります。

### `show`
読み込んだ識別子の一部だけ使えるようにするためには`show`を使います。

## pub
pubはDartのパッケージマネージャです。
Dartで外部パッケージを使用する場合に使われます。
pubspec.yamlというファイルにパッケージの情報を書き`dart pub get`コマンドを実行することでパッケージがインストールできます。
または，`dart pub add <パッケージ名>`とも書けます。このように書いた場合はpubspec.yamlにも同時に書き込まれます。
Flutterの場合は`flutter pub get`コマンドです。
パッケージの更新を行う場合は`get`を`upgrade`にします。

ここらへんはIntelliJ系のIDEであればFlutterプラグインなどでボタン一つで行えるのであまり気にしなくても良いかもしれません。

# 非同期
Dartは非同期処理をサポートしています。
非同期では`Future<T>`というクラスを使用します。`Future<T>`は非同期処理を管理(?)するためのクラスで終了時に呼ばれる関数などを指定することができます。
`Future<T>`か`T`を受け取るという意味の型として`FutureOr<T>`もあります。`Future`は`dart:core`に含まれるため`import`は不要です。`FutureOr`は`dart:async`に含まれています。

## `await`
`await`は非同期な処理の終了を待機することを示す単語です。
`await`を使うためには関数を`async`に指定する必要があります。`async`に指定した関数は返り値の型が`Future`になります。

```dart
Future<void> asyncFunc() async {
  // do something
}

// 使う側
Future<void> asyncCaller() async {
  return await asyncFunc();
}
```

## `async`と`async*`
Dartは`async`の代わりに`async*`と書くことで返り値を`Stream<T>`にすることができます。
`async`と`async*`の関係は、普通の関数と`sync*`の関係と同じです。
`async*`を使うことで`yield`を使うことができるようになります。

また、`Stream<T>`を手軽に扱うためにDartには`await for`という機能があります。
これは`Stream<T>`を`for`と同様の構文で回すための機能です。

```dart
Stream<int> getStream() {...}

Future<void> main() async {
  await for (final n in getStream()) {
    // do something
  }
}
```

# 終わりに
以上、Dartの基本的な機能をさらっと紹介しました。

Dartを手軽に試す方法として[DartPad](https://dartpad.dev/)があるので使ってみてください。