Swift 2でエラーをthrowする関数からEitherを作る

タイトルでは`Either`といいながら片方が`ErrorType`決め打ちなので`Failable`型ということで。

```swift:Failable.swift
enum Failable<T> {
    case Success(T)
    case Failure(ErrorType)
    
    init(@autoclosure _ f: () throws -> T) {
        do {
            self = .Success(try f())
        } catch {
            self = .Failure(error)
        }
    }
}
```
ポイントは`Failable.init(_:)`にエラーを`throw`するクロージャを渡しているところ。

使ってみる。

```swift:Failable.swift
enum MyError : ErrorType {
    case ParseIntError(String)
    case PatternNotFoundError(String, String)
}

func parseInt(text: String) throws -> Int {
    if let num = Int(text) {
        return num
    } else {
        throw MyError.ParseIntError(text)
    }
}

// 普通ならこう
do {
    try parseInt("5")
} catch {
    print(error)
}

// Failableを使うとこうなる
Failable(try parseInt("5"))  // .Success(5)
Failable(try parseInt("x"))  // .Failure(MyError.ParseIntError("x"))
```

引数は`{}`で囲われてないが`@autoclosure`によってクロージャになっている。`try`はクロージャからエラーを投げるために必要。`do`や`catch`で囲う必要がなく、また`try!`のような危険もなく、簡単に失敗する可能性がある処理を扱える。

ちなみにエラーを`throw`する関数は`throws`も型に含まれる。例えば上に出てくる`parseInt`関数の型は`String throws -> Int`型になる。

`map`とかで途中のエラーが伝わっていくやつ。

```swift:Failable.swift
extension Failable {
    func map<U>(f: T -> U) -> Failable<U> {
        switch self {
        case .Success(let v): return .Success(f(v))
        case .Failure(let e): return .Failure(e)
        }
    }
    
    func flatMap<U>(f: T -> Failable<U>) -> Failable<U> {
        switch self {
        case .Success(let v): return f(v)
        case .Failure(let e): return .Failure(e)
        }
    }
}

Failable(try parseInt("5")).map { $0 * $0 }  // .Success(25)
Failable(try parseInt("x")).map { $0 * $0 }  // .Failure(MyError.ParseIntError("x"))
```

エラーを投げてくる`NSRegularExpression.init(pattern:options:)`や`NSString.init(contentsOfFile:encoding:)`を使った例。下のようなテキストファイルをPlaygroundのリソース等に入れておく。

```text:File
Hello, Swift 2!
```

```swift:Failable.swift
func search(pattern: String, filePath: String) -> Failable<String> {
    let regex = Failable(try NSRegularExpression(pattern: pattern, options: []))
    let text = Failable(try NSString(contentsOfFile: filePath, encoding: NSUTF8StringEncoding))
    
    return regex.flatMap { r in
        text.flatMap { t -> Failable<String> in
            if let match = r.firstMatchInString(t as String, options: [], range: NSMakeRange(0, t.length))
                where match.range.location != NSNotFound {
                    return .Success(t.substringWithRange(match.range))
            } else {
                return .Failure(MyError.PatternNotFoundError(pattern, filePath))
            }
        }
    }
}

let path = NSBundle.mainBundle().pathForResource("File", ofType: nil)!
// 正規表現が間違ってる場合
search("[", filePath: path)        // .Failure(... The value “[” is invalid. ...
// ファイルパスが間違ってる場合
search("Swift", filePath: "hoge")  // .Failure(... there is no such file. ...
// ファイルの文字列にパターンがマッチしなかった場合
search("ObjC", filePath: path)     // .Failure(MyError.PatternNotFoundError("ObjC" "..."))
// マッチが成功した場合
search("Swift", filePath: path)    // .Success("Swift")
```

正規表現が間違ってるのか、ファイルが無いのか、正規表現もファイルパスも正しいが文字列が見つからなかったのか、エラーがちゃんと伝わってるのがわかる。

「エラーを投げる関数」を「エラーを投げずに`Failable`を返す関数」に変換する高階関数バージョン。

```swift:Failable.swift
func failable<T, U>(f: T throws -> U) -> T -> Failable<U> {
    return {
        do {
            return .Success(try f($0))
        } catch {
            return .Failure(error)
        }
    }
}

failable(parseInt)("5")  // .Success(5)

search("[0-9]+", filePath: path)
    .flatMap(failable(parseInt))  // .Success(2)
```

エラーを投げる`String throws -> Int`型の`parseInt`関数を、エラーを投げない`String -> Failable<Int>`型の関数に変換して`flatMap`に渡している。

Cocoaが投げてくるエラーを処理するだけならdo-try-catchで十分かもしれない。すでにアプリケーションのコード内でエラー処理に`Either`的なものを使っていて、そこにCocoaから来るエラー処理も組み入れたい場合は、こういう方法が使える。