Swift 2.0 の try, catch ファーストインプレッション

WWDC 2015 で Swift 2.0 が発表されました。オープンソース化などのうれしいニュースでも盛り上がっていますが、言語仕様としては _try_, _throw_, _catch_ が導入されるという大きな変更がありました。本投稿は、 The Swift Programming Language の新章 [Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508) を読み、多少のコードを書いた上での個人的な感想です。

__結論から言うと、 _try_, _catch_ の導入は良い変更だと思えないけど、 _try_, _catch_ を導入する前提なら考え得る限りベストに近い仕様だった、って感じです。__ よかったのは、

- ErrorType は enum
- タイプセーフなエラー情報
- エラー処理が強制されている（検査例外のような形）
- `try!` でエラーを無視できる

あたりです。個人的には、 _try_, _catch_ でなく `Either` 的なものを公式サポートして、 `Optional` ばりの便利な構文を用意してくれたらうれしかったです。

## try, catch を導入する前提なら考え得る限りベストに近い仕様

### ErrorType は enum

Swift の _try_, _catch_ では例外ではなく `ErrorType` を _throw_ します。 Java ではエラーの種類をクラスで扱うのでエラーの種類だけクラスが増えてしまって散らかりやすいです。

Swift の `ErrorType` は関連するエラーを _enum_ としてまとめることができるので整理がしやすそうです。

例えば、 `UILabel` から受け取った文字列を `Int` に変換する関数 `toInt` を考えてみましょう。 `UILabel` の `text` プロパティの型は `String?` なので、エラーには次の 2 種類が考えられます。

- 文字列が `nil` だった
- 文字列が `Int` としてパースできなかった

これを `ErrorType` として実装すると次のようになります。

```swift
enum ParseError: ErrorType {
    case Nil // nil だった場合
    case IllegalFormat // Int としてパースできなかった場合
}
```


### タイプセーフなエラー情報

_ErrorType_ には、エラーの種類を表すだけでなく、エラー情報も保持してほしいところです。

先の例で言えば、 `IllegalFormat` のときに元の文字列が何だったのか知りたいこともあるでしょう。次のようにして、 `IllegalFormat` に文字列を保持させることができます。

```swift
enum ParseError: ErrorType {
    case Nil
    case IllegalFormat(String)
}
```

この方式の素晴らしいところは、エラーの種類ごとに異なる型を持たせて、それらをタイプセーフに取り出すことができる点です。

```swift
switch error {
case .Nil:
	print("The text is nil.")
case .IllegalFormat(let string):
	print("Illegal format: \(string)")
}
```

Objective-C の `NSError` にエラー情報を持たせることができますが、型情報が消えてしまうのでタイプセーフに扱えません。

### エラー処理が強制されている（検査例外のような形）

Swift の _try_, _catch_ で一番おどろいたのは、 Java の _検査例外_ と似たような方式だったことです。

_検査例外_ とは例外を _throw_ する可能性のある関数・メソッドをコールするときに、処理しないとコンパイルエラーになってしまうような例外です。対義語は _非検査例外_ で、 _非検査例外_ は処理しなくてもコンパイルエラーとならず、実行時に処理されない例外が発生すると実行時エラーとなります。

_検査例外_ はエラー処理を強制するのでエラー処理忘れが発生しませんが、必ず成功することがわかっている場合でもエラー処理が必須だったり、コードが本質ではない _try_, _catch_ だらけになってしまったりで、 Java 以外のメジャーな言語には取り入れられてきませんでした。

```java
// Java でありがちな try, catch だらけのコード
BufferedReader reader;
try {
	reader = ...;

	String line;
	while ((line = reader.readLine()) != null) {
		System.out.println(line);
	}
} catch(IOException e) {
	e.printStackTrace();
} finally {
	if (reader != null) {
		try {
			reader.close()
		} catch( IOException e) {
			e.printStackTrace();
		}
	}
}
```

また、 _検査例外_ と _非検査例外_ の境界もあいまいでした。例えば、 Java の `Integer` クラスの `parseInt` メソッドは文字列をパースして `int` に変換するのですが、失敗したときに　_throw_ される `NumberFormatException` は _非検査例外_ でした。パースに失敗したかで分岐して処理するのは頻繁に書く処理なので、僕は、エラー処理を忘れないように `NumberFormatException` には _検査例外_ であってほしいと思います。

かといって、すべてを _検査例外_ にすると、↑のコードよりもひどいことになってしまいます。例えば、整数のわり算でも 0 除算をするかもしれないので _try_, _catch_ しなければならなくなります。

そんなわけで、 _検査例外_ 自体はタイプセーフな優れたアイデアだけれども、現実的には何か新しい記法が見つからないと取り入れるのは辛いというのが一般的な認識だと思っていました（ソースがなくてすみませんが、確か C# の設計者も _検査例外_ を取り入れなかった理由としてそんなことを語ってた気がします）。

なので、 Swift が _検査例外_ 的な方式を採用したのには驚きました。一方で、もし Swift が _非検査例外_ を取り入れてたらひどいことになっていたと思います。 Swift はその頑強な型システムが特徴の一つなので、 _非検査例外_ を導入したらその頑強性が台無しです。 __Swift が _検査例外_ 的な方式を採用したのは _try_, _catch_ の仕様として最も良かった点だと思います。__

```swift
do {
	let number = try toInt(label.text)

	// number を使った処理

} catch ParseError.Nil {
	print("The text is nil.")
} catch ParseError.IllegalFormat(let string) {
	print("Illegal format: \(string)")
}
```

また、 `Optional` と共存することで、軽微なエラー処理は `Optional` で、複雑なエラー処理は _try_, _catch_ でと使い分けることができるので、 Java ほど _try_, _catch_ だらけになることもないと思います。

### try! でエラーを無視できる

では、何があれば _検査例外_ がより実用的になるのでしょうか。

以前そのテーマで同僚と話していたのですが、最低限、例外を無視するシンプルな構文が必要だという話になりました。例えば、数字しか入力できないフォームから得た文字列をパースするときに、絶対に起こらないエラー処理を書かなければならないのはナンセンスです[^1]。

```java
// ※ Java の NumberFormatException が検査例外だったとする
int number;
try {
	number = Integer.parseInt(text);
} catch(NumberFormatException e) {
	throw new Error("ここには絶対に来ない");
}
```

そのときは、例えば `!` 一つで無視して、もし例外が起こったら実行時エラーという構文があるといいね、という結論になりました。

```java
// Java に ! があるイメージ
int number =  Integer.parseInt(text)!;
```

Swift の _Forced Unwrapping_ はまさにこのような構文で「これだよ！」と思いました。後置の　`!` は _Forced Unwrapping_ にとられてしまいましたが、 `try` にも `try!` という構文が提供されました。これを使えば簡単にエラーを無視することができます。

```swift
let number = try! toInt(label.text) // エラー時はクラッシュ
```

また、 `try` や `try!` が演算子なのも素晴らしいです。もし `try` 文だったとすると、複数の値を使って何かをやるのはとても大変です。

```swift
// ※これは正しい Swift のコードではありません
try! let foo = foo()
try! let bar = bar()
let result = foo + bar
```

`try` や `try!` が演算子なことで、次のように一つの式の中に書くことができます。

```swift
let result = try! foo() + (try! bar())
```

~~あと足りなかったものとして、 `try?` があるとよかったですね。エラーの種類やエラー情報は不要で成功したか失敗したかだけが知りたいというケースも多いはずです。 `try!` では失敗したときに即死してしまうので、エラー処理するには _do-catch_ するしかないのですが、それだと式ではなく文になってしまい辛いです。エラーは `nil` として返してくれる `try?` があればよかったのに・・・。~~

Xcode 7 beta 6 で `try?` 演算子が追加されました！素晴らしい！！

なので↓の `tryq` は不要になりました。

#### try? のような tryq を作ってみた

`try?` は将来的に提供されるんじゃないかと思いますが、ないと不便なので `try?` の代わりに使える `tryq` という関数を作ってみました。 → [GitHub](https://github.com/koher/tryq)

```swift
public func tryq<T>(@autoclosure f: () throws -> T) -> T? {
    do {
        return try f()
    } catch {
        return nil
    }
}
```

次のように使えます。

```swift
// let result: Int? = try? foo(42)
let result: Int? = tryq(try foo(42))
```

これは、下記のコードと同じ結果となります。 `tryq` を使えば、成功か失敗かを知りたいだけのときはとてもシンプルに書けます（ `tryq` に加えてわざわざ `try` を書かないといけないのが微妙ですが・・・）。

```swift
let result: Int?
do {
    result = try foo(42)
} catch {
    result = nil
}
```

また、次のように式の中でも使えます。これを _do-catch_ でやろうとすると相当面倒です。

```swift
// let result = (try? foo(42)).map { $0 * $0 }
let result = tryq(try foo(42)).map { $0 * $0 }
```

## try, catch の導入は良い変更だと思えない

僕はエラーや例外を _throw_ する構文が好きではありません。理由はいくつかありますが、一番の理由はコードを頭から順に書くのが難しいことです。例えば、

```swift
let result = foo().bar().baz()
```

と書いて、 `baz` がエラーを _throw_ するので構文エラーになったとします。そうすると、前の行に戻って `do` を書き足して変数宣言を `{ }` の外に出してというように、後戻りしてコードを書かなければなりません。これはとても面倒です。

```swift
var result: Baz
do {
	result = try foo().bar().baz()
} catch {
	// エラー処理
}
```

結果的に、細かく _do-catch_ するのは面倒なので、大きすぎる _do_ 文の中で複数の _try_ が書かれてまとめて _catch_ するというコードであふれてしまうのではないでしょうか。まとめて _catch_ するとエラー処理も適当になりがちです。

`Optional` ならひとまずは

```swift
let result = foo().bar().baz() // baz に失敗したら nil
```

としておいて、利用する時にエラー処理を書けば OK です。 `Optional` の `nil` チェックは必須なので、エラー処理を忘れてしまうこともありません。不必要に複数のエラー処理がまとめられてしまうことないですし、 `map` や `flatMap` を使えば自然な順序でコードを書けます。

僕は _try_, _catch_ ではなく `Optional` でエラー処理することは進化だと感じ、 `Optional` の取り扱いが重要だと感じたからこそ ["SwiftのOptionalを極める"](http://qiita.com/koher/items/c6f446bad54442a28bf4) を書きました。なので、 _try_, _catch_ に後戻りするのは気乗りしないです。

### Optional の問題点と Either

`Optional` の問題点はエラー情報を保持することができないことです。エラーの種類によって処理を変えたいような場合には `Optional` では不十分です。しかし、 _try_, _catch_ でないと問題を解決できないわけではありません。

一部の言語ではそのような目的に `Either` が用いられます[^2]。

```swift
enum Either<T, U> {
	case Left(T)
	case Right(U)
}
```

`Optional` が `T` か `nil` かを持つのに対して、 `Either` は `T` か `U` の値を持ちます。この `U` にエラー情報を持たせればエラー情報を持った `Optional` のようなものを作れます。エラー処理に特化すれば、次のような型でもいいかもしれません。

```swift
enum Failable<T, E: ErrorType> {
	case Success(T)
	case Failure(E)
}
```

これなら、 `Optional` 同様に　`if let ...` で _bind_ できて、　`?` でチェーンできるなども自然です。さらに、 `Foo?` が `Optional<Foo>` を表すように、 `Foo|BarError` が `Either<Foo, BarError>` や `Failable<Foo, BarError>` を表すなんてことも考えられたでしょう。

僕がほしかったのは↓のようなものです。

```swift
// ※これは正しい Swift のコードではありません
let a: Int|ParseError = toInt(label.text)

if let value = a {
	print(value)
}

switch a {
case .Success(let value):
	print(value)
case .Failure(.Nil):
	print("The text is nil.")
case .Failure(.IllegalFormat(let string)):
	print("Illegal format: \(string)")
}

let b = a.map { $0 * $0 }
let c = a.flatMap { a0 in b.flatMap { b0 in a0 + b0 } }
let d: Int = a ?? 0
let e = a?.successor()
```

ただ、値を返さないようなメソッド、副作用のある（値を返すだけでなく、プロパティを変更するなどする）メソッド等では `Optional` や `Either` では対応できません。あらゆるケースにおけるエラー処理をカバーするためには、現時点では　_try_, _catch_ は必要なのかもしれません。

___（追記: 2015-12-08 ） Swift がオープンソース化されたので、試しに `Int|String` のような構文が使えるように[コンパイラを改造してみました](http://qiita.com/koher/items/aea1b44d0aef43b0059c)。___

___（追記: 2015-09-10 ）次期 Swift で `Either` が追加されるかもしれないようなので、 `Either` と do-try-cath についてもっと掘り下げて ["Swift 3.0で追加されそうなEitherについて"](http://qiita.com/koher/items/288f01cddb0bbb6aa4e1) を書きました。___

### （おまけ） Swift 2.0 で Failable を作ってみました

___（追記: 2015-08-30 ）デファクトになっている [antitypical/Result](https://github.com/antitypical/Result) が Swift 2.0 対応で `ErrorType` に対応し、下記の `Failable` と同等のものになっているので、ライブラリとして使う場合はそちらをオススメします。下記は、 try 等を学ぶためのものとして役立てて下さい。___

言語仕様に組み込まれたサポートが受けられないので限界はありますが、 Swift 2.0 で↑の `Failable` を作ってみました（ → [FailSwif](https://github.com/koher/FailSwif) ）。↑のコードと見比べると、何が実現できて何ができなかったのか、僕がほしかったのは何なのかわかっておもしろいと思います。

また、[テストコード](https://github.com/koher/FailSwif/blob/master/FailSwifTests/FailSwifTests.swift) では `try`, `catch`, `throw`, `guard` なども使っているので Swift 2.0 のサンプルコードとしてもどうぞ。

```swift
let a: Failable<Int, ParseError> = toInt(label.text)

if let value = a.value {
	print(value)
}

switch a {
case .Success(let value):
	print(value)
case .Failure(.Nil):
	print("The text is nil.")
case .Failure(.IllegalFormat(let string)):
	print("Illegal format: \(string)")
}

let value: Int? = a.value
let error: ParseError? = a.error

let b = a.map { $0 * $0 }
let c = a.flatMap { a0 in b.flatMap { b0 in .Success(a0 + b0) } }
let d: Int = a ?? 0
let e = a.flatMap { .Success($0.successor()) }
```

--------------------------

[^1]: Java の `NumberFormatException` が _検査例外_ だったら嫌だということではなく、安全な方が望ましいのであくまで　_検査例外_ であってほしいけど、その上で簡単に例外を無視する構文が欲しいという意見です。

[^2]: Swift 2.0 から `Box` で包むようなワークアラウンドは必要なくなりました。