まだSwiftyJSONで消耗してるの？

結論: __[Argo](https://github.com/thoughtbot/Argo) すげー！！特に Swift 2.0 以降で。__

## はじめに

[Qoncept](http://qoncept.co.jp/) では週に 2 回、みんなでコードリーディングする時間を設けています。先週から [Carthage](https://github.com/Carthage/Carthage) のコードを読み始めたところ、依存ライブラリの中に [Argo](https://github.com/thoughtbot/Argo) という JSON パーサを見つけました。

僕はこれまで JSON のデコードに [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) を使っていました。しかし、 Argo の [README](https://github.com/thoughtbot/Argo/blob/master/README.md) やソースを読んでみて、これは素晴らしいと思ったので Argo について紹介します。

## JSONのデコード

次のような `User` 型があるとします。

```swift
struct User {
    let id: String
    let nickname: String?
    let age: Int
    let admin: Bool
}
```

JSON をデコードして `User` のインスタンスを生成する方法を考えましょう。

```js
{
	"id"       : "a082b4fe",
	"nickname" : "Foo",
	"age"      : 30,
	"admin"    : false
}
```

現実的なケースに近づけるために、下記の条件も加えます。

- `age` が負のときはデコード失敗（ __バリデーションを行う__ ）
- `admin` が省略されたときは `false` とする（ __デフォルト値を設定する__ ）

### SwiftyJSON

まずは SwiftyJSON でデコードしてみましょう（ Swift で直接 `NSJSONSerialization` を使うのは面倒すぎるので、その選択肢は考えません）。

`JSON` を受け取り `User` を返すメソッドを考えます。デコードに失敗することもあるので、失敗したら `nil` を返すことにしましょう[^1]。

```swift
extension User {
    static func from(json: JSON) -> User? {
        guard let id = json["id"].string else {
            return nil
        }

        let nickname = json["nickname"].string

        guard let age = json["age"].int where age >= 0 else {
            return nil
        }

        let admin = json["admin"].bool ?? false

        return self.init(id: id, nickname: nickname, age: age, admin: admin)
    }
}
```

### SwiftyJSON（エラー情報付）

上記の方法では、 `nil` が返ってきたときにデコードに失敗したことはわかりますが、失敗した原因まではわかりません。どこで失敗したかわからないとデバッグが大変だったり、適切なエラー処理ができなかったりします。失敗したときは `nil` を返すのではなく、エラー情報を付与して返すことにしましょう。

`Optional` のように失敗を表したい、でも失敗したことだけじゃなくて何らかの情報を付与したい、そんなときに Swift でよく使われるライブラリが [Result](https://github.com/antitypical/Result) です。 `Result` は `Optional` を拡張したような型で、成功時の値に加え、失敗時のエラー情報も保持できる次のような `enum` です。

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

`Optional` の宣言と見比べるとよく似ているのがわかります[^2]。違いは、失敗のときにエラー情報を保持できるかできないかです。

```swift
enum Optional<T> {
    case Some(T)
    case None
}
```

`Result` は `Optional` 同様モナドとなるよう設計されており、 `map` や `flatMap` などのメソッドを使うことができます。また、 `??` 演算子も用意されており、まさに強化 `Optional` という感じです。

では、 `Result` を使って `from` を書きなおしてみましょう。

```swift
extension User {
    static func from(json: JSON) -> Result<User, ParseError> {
        guard let id = json["id"].string else {
            return .Failure(.Unparsable("id"))
        }

        let nickname = json["nickname"].string

        guard let age = json["age"].int where age >= 0 else {
            return .Failure(.Unparsable("age"))
        }

        let admin = json["admin"].bool ?? false

        return .Success(self.init(id: id, nickname: nickname, age: age, admin: admin))
    }
}

enum ParseError: ErrorType {
    case Unparsable(String)
}
```

`Optional` のときとほぼ変わらない複雑さでエラー情報を扱えるようになりました。 `age` が存在しなかった場合と負だった場合はエラーの種類を分けてもいいのですが、例としてはややこしくなるだけなので、ここではまとめて扱います。

### Argo

SwiftyJSON が良くないのは、次の 3 点です。

1. `User` のプロパティを宣言するときとデコードするときの __計 2 回、型を記述しなければならない__。
2. どのキーについてエラーが発生したのか記述しようとすると、値を取得するときとエラー情報を記述するときの __計 2 回、キーを記述しなければならない__。
3. イニシャライザにパラメータを渡したいだけ（本来 1 行のコード）なのに、パラメータを一つずつチェックしてエラー処理する必要があるのでコードが長くなる。

1 については、例えば `User` 宣言時に `let id: String` であると宣言しているにも関わらず、 `id` をデコードするときに `json["id"].string` と、再び `String` であることを記述しなければならないことを言っています。

2 は、値を取得するときの `json["id"]` と、エラーだった場合の `.Unparsable("id")` で同じキーを記述しなければならないということです。

Argo を使えばそれらを解決し、シンプルに記述できます[^3]。

```swift
extension User: Decodable {
    static func decode(json: JSON) -> Decoded<User> {
        return curry(User.init)
            <^> json <| "id"
            <*> json <|? "nickname"
            <*> (json <| "age" >>- { $0 >= 0 ? pure($0)
                : .customError("age: Out of range (\($0))") })
            <*> (json <|? "admin").map { $0 ?? false }
    }
}
```

`<|`, `<|?`, `>>-`, `<^>`, `<*>` など意味不明ですね！順に見ていきましょう。

なお、 `User: Decodable` となっているのは Argo の仕様で、 JSON から Argo を使ってデコードできる型は必ず `Decodable` でなければなりません。 `Decodable` は上記のような `decode` メソッドを実装する必要があります。

`Decoded` は前述の `Result` のような型で、デコードの成功か失敗かを表します。また、成功の場合は結果（この場合は `User` のインスタンス）を、失敗の場合はエラー情報を保持します。詳細は後述します。

#### <|

`<|` はキーを指定して `JSON` から値を取得する演算子です。

例えば `json <| "id"` と書くと、 `json` から `"id"` をキーとして値を得ることができます。 SwiftyJSON では `json["id"].string` のように型の指定が必要だったのに対して、 Argo では型を指定しないことに注目して下さい。 __`<|` はジェネリックな演算子なので、戻り値の型を推論して自動的に型を決定します__。そのため、型の記述が必要ないわけです。

JSON のデコードは常に失敗する可能性があるため、生の値（ `"id"` なら `String` ）をそのまま返すわけにはいきません。 `<|` で得られる値は `Decoded` でラップされて返されます。 `Decoded` は `Result` と同じような役割の型ですが、 Argo 専用に設計されています。

```swift
enum Decoded<T> {
    case Success(T)
    case Failure(DecodeError)
}

enum DecodeError: ErrorType {
    case TypeMismatch(expected: String, actual: String)
    case MissingKey(String)
    case Custom(String)
}
```

`Decoded` と `Result` との違いは次の 2 点です。

- エラーの型が `DecodedError` に限定されている。
- （そのため）型パラメータが `T` 一つしかない。

エラーとして `DecodedError` しか持つことができないので `Result` ほど汎用ではありませんが、 `Result<T, Error>` のように型パラメータを二つ保持しなくて良いのでコードがすっきりします。 JSON のデコードで起こり得るパターンは決まっているのでそれに特化してしまった方が良いという判断でしょう。

`Optional` や　`Result` と同じく `Decoded` もモナドとして設計されており、 `map` や `flatMap` が使えます。

次に、 `DecodeError` の中身に目を向けると `.TypeMismatch` は `Int` としてデコードしようとしたけど JSON 上は文字列だったなど、型の不整合が起こった場合に返されます。 `.MissingKey` はそもそも指定されたキーが存在しなかった場合に返されます。 `.Custom` はその他のエラー、例えば、デコードには成功したけどその値が不適切だった場合などに Argo のユーザーが自分で生成して返すためのものです。

#### <|?

`<|?` は `<|` と似た働きの演算子ですが、キーが存在しなかった場合でも `.MissingKey` になりません。 `Optional` なプロパティをデコードする場合に `.MissingKey` になってしまうと困るので、このような演算子が別途用意されています。

プロパティが `Optional` であることから自動判別してくれれば良いように思えるかもしれませんが、 __プロパティが `Optional` でなくても `.MissingKey` になっては困る場合があるので `<|?` は必須です__。 `admin` プロパティはその例です。

管理者権限を持っているかを表す `admin` プロパティは、 `let admin: Bool` と宣言されているので `nil` は許されず、必ず `true` か `false` を指定する必要があります。しかし JSON 上では `"admin"` というキーは省略することができるということにしたので、省略された場合は `false` として扱わなければなりません。

そこで、まず `<|?` を使って `"admin"` を取得します。 `json <|? "admin"` の時点では戻り値の型は `Decoded<Bool?>` となります。これを `Decoded<Bool>` に変換し、もし `Decoded` が保持している値が `nil` だった場合は `false` にしてしまいたいわけです。

そんなときは `map` の出番です。 `map` は `Array` や `Optional` でもおなじみですが、 `Foo<T>` を `Foo<U>` に変換するために使われるメソッドです（詳細は ["SwiftのOptional型を極める"](http://qiita.com/koher/items/c6f446bad54442a28bf4) ）。今は `T` が `Bool?` で　`U` が `Bool` となります。

実際に書くと次のようになります。

```swift
(json <|? "admin").map { $0 ?? false }
```

これは `Decoded<Bool?>` の　`map` なので `$0` の型は `Bool?` です。 `??` 演算子の戻り値の型は `Optional` が取れるので `Bool` となり、 `map` の結果は　`Decoded<Bool>` となります。また、 `$0 ?? false` で `nil` は `false` に変換されるわけです。

#### >>-

`>>-` は `flatMap` の演算子版です。元々は　Haskell の `>>=` から来ているのですが、 Swift では `>>=` 演算子は複合代入演算子に割り当てられているので、代わりに `>>-` とするのが一般的[^4]です。

`>>-` は単に `flatMap` を演算子にしただけなので、次の二つは等価です。

```
foo.flatMap { ... }
foo >>- { ... }
```

では、何のために `>>-` はあるのでしょうか。僕の推測ですが、おそらく次のような理由からです。

1. `flatMap` は Swift 1.2 で導入されたので、それ以前にすでに `>>-` が流行っていた。
2. Haskell に慣れた人が似たような記法で書きたかった。
3. `>>-` で書いた方が `flatMap` よりも可読性が良い。

1, 2 はともかく、 3 は今でも Swift で `>>-` を使う理由になり得ます。実際に `flatMap` を多用するとわかりますが、コードが非常に読みづらくなるケースがあります。

可読性が低下する原因の一つは、僕はシンタックスハイライトの問題だと考えています。 Trailing Closure を使う場合、意味的には制御構文のような働きをしていることが多いのですが、制御構文と異なりキーワード（ `flatMap` の部分）がハイライトされないので、ぱっと見て構造を把握するまでに時間がかかります（メソッドと制御構文的なものが混ざってしまうので）。 `>>-` というメソッドの識別子とはまったく違う見た目のもので記述されていると、ひと目で式の構造を読み取れるようになります。これについては、 Trailing Closure を使った場合は制御構文同様にシンタックスハイライトしてくれるようになれば解決するかもしれません。

また、複数の種類のモナド（ `Optional` と `Array` とか）を混ぜて使う場合に、どっちの `flatMap` なのかわかりづらくなることもあります。そういう場合は、僕は外側を `>>-` にして区別することが多いです（ `>>-` で書かれた大きな区切りの中に `flatMap` による小さな式が入るイメージです）。具体的には、 [PromiseK](https://github.com/koher/PromiseK) で `Promise` をチェーンする場合に使うことが多いです。内側で `Optional` の　`map` や `flatMap` を使うことが多いですが、 `>>-` で処理をつなぐとそれらと混ざってしまうこともないですし、処理をチェーンして記述する `Promise` の目的とも相性が良いように感じています。

##### バリデーションとflatMap

下記の `age` の取得およびバリデーションについては、 `Decoded` 関連の一連の処理ということで `<|` とレベル間を併せておいた方が直感的にわかりやすいので `flatMap` ではなく `>>-` を使っています。

```swift
json <| "age" >>- {
    $0 >= 0 ? pure($0) // pure は .Success と同じ意味の関数
            : .customError("age: Out of range (\($0))") }
}
```

`flatMap` も `map` 同様 `Foo<T>` を　`Foo<U>` に変換するために使われます。 `map` との違いは、 `flatMap` に渡す関数が `U` を返すのではなく `Foo<U>` を返すことです。

例えば、バリデーションのように値の取得自体に成功しても、条件に合わなければ失敗にしたい場合などは `map` では表せません。 `map` だと `.Success` は `.Success` のまま値を変換するだけで、　`.Success` を `.Failure` にすることはできないのです。 `flatMap` を使えば `Foo<U>` そのものを返すことができるので、 `.Success` の場合でも　`.Failure` にしてしまうことができます。

`.customError` は　`Decoded` の持つ _static_ メソッドで、 `.Failure(.Custom)` を生成します。 JSON のデコードという目的を考えると、このケース（ `"age"` が負だった場合）のようにバリデーションの失敗を表すために使うことが多いのではないかと思います。

`pure` についてはコメントを付けましたが、これも　Haskell 由来の関数で、ただ単に値を `Decoded` などで包むだけの関数です。 `Decoded` なら `.Success` に、 `Optional` なら `.Some` になります[^5]。

#### <^>, <*>

##### アプリカティブスタイル

`<^>` と　`<*>` はアプリカティブスタイルを実現するための関数です。

アプリカティブスタイルの原理を詳しく説明するとそれだけで投稿一本分になってしまうので、ここでは何をしたいかだけを簡単に説明します。

例えば、次のような関数があったとします。単に三つの `Int` を足すだけのシンプルな関数です。

```swift
func add3(a: Int, b: Int, c: Int) -> Int {
    return a + b + c
}
```

このとき、次の `x`, `y`, `z` を `add3` に渡すにはどうすれば良いでしょうか？どれも `Optional` なのでそのままでは渡せません。

```swift
let x: Int? = ...
let y: Int? = ...
let z: Int? = ...
```

`<^>` と `<*>` 、それに `curry` 関数を使えば次のように書けます（ [Runes](https://github.com/thoughtbot/runes) と [Curry](https://github.com/thoughtbot/Curry) を使います）。

```swift
let result: Int? = curry(add3) <^> x <*> y <*> z
```

もし、　`x = 2`, `y = 3`, `z = 5` なら `result` は　`Optional(10)` になります。 `x`, `y`, `z` の一つでも `nil` であれば　`result` も　`nil` になります。

このように、 `<^>` と `<*>` を使えば、 引数が `Optional` でない関数やメソッドに `Optional` な値を引数として渡すことができます。このような記法を __アプリカティブスタイル__ と呼びます。 `Decoded` も強化版 `Optional` のようなものなので同じことができます[^6]。

この `<^>` と `<*>` も Haskell 由来の演算子です。 `<^>` は Haskell では　`<$>` という演算子なんですが、 Swift では `$` を含む演算子を作ることができないので `<^>` とすることが一般的です。

##### カリー化

`curry` は __カリー化__ を行う関数です。 `<^>` や `<*>` を適用するには関数がカリー化されている必要があります。カリー化とは N 引数の関数を 1 引数ずつ適用する高階関数に変換する処理です。例えば、次の `curriedAdd3` がカリー化された `add3` の例です。

```swift
func curriedAdd3(a: Int) -> Int -> Int -> Int {
	return { (b: Int) -> Int -> Int in
	    { (c: Int) -> Int in
	        a + b + c
	    }
	}
}

curriedAdd3(2)(3)(5) // 10
```

まず、 `curriedAdd3(2)` を実行すると、 `Int -> Int -> Int` という関数が得られます。これは　`Int -> (Int -> Int)` と考えるとわかりやすいです。つまり、引数として　`Int` を渡せば `Int -> Int` という関数が戻り値として得られる関数です。

その `Int -> (Int -> Int)` の関数に `(3)` を適用して `Int -> Int` という関数を得、最後にその関数に `(5)` を適用して戻り値の `10` という `Int` が得られるわけです。


Swift では、次のように簡単にカリー化された関数を宣言する方法もあります。

```swift
func curriedAdd3(a: Int)(_ b: Int)(_ c: Int) -> Int {
    return a + b + c
}

curriedAdd3(2)(3)(5) // 10
```

[Curry](https://github.com/thoughtbot/Curry) を使えば、 16 引数までの関数をカリー化することができます。

##### Decodedとイニシャライザとアプリカティブスタイル

今やりたいのは、 `User` のイニシャライザに `<|` や `<|?` で得られた値を渡すことです。しかし、それらの値は生の値ではなく `Decoded` でラップされているので、そのまま渡すことはできません。そこで、アプリカティブスタイルです。

`curry(User.init)` でイニシャライザをカリー化して、それに `<^>` と `<*>` を使って `Decoded` な値を適用していきます。つまり、構造上は次のようになっています。

```swift
curry(User.init) <^> id <*> nickname <*> age <*> admin
```

こうすれば、一つ一つのパラメータは `Decoded` であっても、それらをアンラップして中の値を取り出さなくてもイニシャライザに渡すことができます（ `Optional` と `add3` の話と同じです）。

ここまでの話を一つにまとめると、最初に書いた式が得られます。

```swift
curry(User.init)
    <^> json <| "id"
    <*> json <|? "nickname"
    <*> (json <| "age" >>- { $0 >= 0 ? pure($0)
        : .customError("age: Out of range (\($0))") })
    <*> (json <|? "admin").map { $0 ?? false }
```

一見わけのわからない記号のかたまりに見えますが、その意味を理解した上で読めばとてもシンプルな構造になっていることがわかります。単にキーを一つずつ指定し、仕様にそってバリデーションしたりデフォルト値を与えたりしているだけです。

またおもしろいのは、アプリカティブスタイルで書くことで `User` のイニシャライザの引数の型から　`<|` や　`<|?` の戻り値の型が推論され、 SwiftyJSON の `json["id"].string` のように型を明示的に指定しなくて良いことです。アプリカティブスタイルなどで型を推論させなければ Argo でも次のように型の指定が必要です。

```
let id: Decoded<String> = json <| "id"
```

なお、 Swift 1.2 まではイニシャライザを直接引数に渡すことができなかったので、いちいちイニシャライザ相当の関数を作る必要があり、 Argo を使うのはやや面倒でした。 Swift 2.0 でその点が解決され、 Argo は素晴らしいライブラリになりました。

## おまけ

前述の三つの問題のうち　3 については、実は SwiftyJSON でもアプリカティブスタイルで解決できます。

### SwiftyJSON（アプリカティブスタイル）

エラー情報を返さず、 `nil` でエラーを表す場合をアプリカティブスタイルで書いてみましょう。 `Optional` で `<^>` と `<*>` を使うためには [Runes](https://github.com/thoughtbot/runes) を使います。

```swift
extension User {
    static func from(json: JSON) -> User? {
        return curry(User.init)
            <^> json["id"].string
            <*> .Some(json["nickname"].string)
            <*> (json["age"].int >>- { $0 >= 0 ? $0 : nil })
            <*> (json["admin"].bool ?? false)
    }
}
```

エラー情報は扱えないですし、いちいち値の型を指定しなければなりませんが、なかなかシンプルです（これまで僕はこのスタイルで書いてました）。

### SwiftyJSON（アプリカティブスタイル、エラー情報付）

次は `Result` を使ってエラー情報を返す場合をアプリカティブスタイルで書いてみましょう。 `Result` には残念ながら `<^>` と　`<*>` がないので自分で実装します。

```swift
extension User {
    static func from(json: JSON) -> Result<User, ParseError> {
        return curry(User.init)
            <^> json["id"].string
                .map { .Success($0) } ?? .Failure(.Unparsable("id"))
            <*> .Success(json["nickname"].string)
            <*> (json["age"].int >>- { $0 >= 0 ? $0 : nil })
                .map { .Success($0) } ?? .Failure(.Unparsable("age"))
            <*> .Success(json["admin"].bool ?? false )
    }
}

func <^><T, E: ErrorType, U>(lhs: T -> U, rhs: Result<T, E>)
    -> Result<U, E> {
    return rhs.map(lhs)
}

func <*><T, E: ErrorType, U>(lhs: Result<T -> U, E>, rhs: Result<T, E>)
    -> Result<U, E> {
    return lhs.flatMap { rhs.map($0) }
}
```

書くことはできますが、これはちょっと辛いですね。 SwiftyJSON はすべてを `Optional` で返すので、 `Optional` から `Result` への変換のために `.map { .Success($0) } ?? .Failure(.Unparsable("id"))` のようなコードが頻出するのが辛いです。これを関数にしてしまう手もありますが、それだったら最初から Argo を使った方が良いでしょう。

## Swift 2.0 での各種ライブラリのインストール

_※ Xcode 7 と Swift 2.0 がリリースされて下記のような問題は解消されました。新しい Cartfile などは [GitHub](https://github.com/koher/Argo-vs-SwiftyJSON) のリポジトリを御覧下さい。_

本投稿では多くのライブラリを紹介しました。

- [Argo](https://github.com/thoughtbot/Argo)
- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
- [Runes](https://github.com/thoughtbot/runes)
- [Curry](https://github.com/thoughtbot/Curry)
- [Result](https://github.com/antitypical/Result)

~~現時点（ 2015 年 8 月 9 日）で、これらのライブラリを Swift 2.0 で使おうとすると、どのバージョンをとってきたらいいのか迷うのではないかと思います。また、 Curry については Prebuilt 版の Framework が配布されているのですが、 Xcode のバージョンが変わると（ Beta 版同士でも）エラーになってしまいハマりました[^7]。~~

~~下記の [Cartfile](https://github.com/koher/Argo-vs-SwiftyJSON/blob/master/Cartfile) および　[Cartfile.resolved](https://github.com/koher/Argo-vs-SwiftyJSON/blob/master/Cartfile.resolved) を使えば、 Swift 2.0 （ Xcode 7 beta 5 ）で動作するバージョンを全ライブラリについて取ってくることができます。両ファイルをコピーして、 [Carthage](https://github.com/Carthage/Carthage) で `carthage bootstrap` すれば OK です（ Carthage については[こちら](http://qiita.com/koher/items/a3b254d0195669088e40)の投稿の中で説明しています）。~~

```:Cartfile
github "thoughtbot/Argo" "td-swift-2"
github "thoughtbot/Curry" "master"
github "thoughtbot/Runes" "master"
github "SwiftyJSON/SwiftyJSON" "xcode7"
github "antitypical/Result" ~> 0.6
```

```:Cartfile.resolved
github "thoughtbot/Argo" "c936846acde9d877cdb2352eb2ee206f830a29e5"
github "thoughtbot/Curry" "367f140c27b79305662748a07bf827da4bb64d32"
github "antitypical/Result" "0.6-beta.1"
github "thoughtbot/Runes" "13fc6356b6d82d4cc68f8ca925a1c12bd19c3a1e"
github "SwiftyJSON/SwiftyJSON" "45ca854ce75948858f65e31fbc58cbf214669250"
```

~~Xcode 7 の Beta 版でビルドするために、 `carthage bootstrap` の前に下記コマンドを実行しておきましょう。~~

```bash
sudo xcode-select -s /Applications/Xcode-beta.app/Contents/Developer
```

~~なお、上記 Cartfile を含む、本投稿関連の実際に動かせるコードを GitHub に [push してあります](https://github.com/koher/Argo-vs-SwiftyJSON)。うまくライブラリがインストールできないときのご参考にお使い下さい。~~

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

[^1]: 厳密にはバリデーションをイニシャライザで行うべきですが、エラー情報を付与することを考えると複雑になってしまうので、本投稿では JSON デコード時にのみバリデーションを行います。
[^2]: `Result` と比較しやすいように、意図的に `.Some` と `.None` の順番を入れ替えています。
[^3]: 本投稿で取り上げた SwiftyJSON 版のコードと Argo 版のコードは厳密には等価ではありません。 Argo 版では `.TypeMismatch` はエラー扱いでしたが、 SwiftyJSON では `.TypeMismatch` と　`.MissingKey` を区別することができないため、 `nickname` や `admin` が `.TypeMismatch` なときでもエラーとならないからです。どちらが望ましいかは仕様次第だと思います。
[^4]: [Swiftz](https://github.com/typelift/swiftz) や [Runes](https://github.com/thoughtbot/runes) など。
[^5]: `Optional` の `pure` 関数は [Runes](https://github.com/thoughtbot/runes) で提供されています。
[^6]: より正確には `Optional` も `Decoded` もアプリカティブファンクターだからです。
[^7]: Prebuilt 版を配布しているのは [Curry](https://github.com/thoughtbot/Curry) のビルドが（おそらく複雑な型推論のため）遅いからだと思います。僕が [ApplicativeSwift]() で同じく　`curry` 関数を実装したときにも同じ問題が発生し、最初は　31 引数版まで作っていたのですが、ビルドが終わらないので　15 引数まで減らしました。 Curry も [17 引数からはコメントアウト](https://github.com/thoughtbot/Curry/blob/f845c353ee1571e701c359a2a8a42760d93da79e/Source/Curry.swift#L57-L101)されており、同様の問題が発生したようです。 Swift は　Literal Convertible があるので型推論が複雑で辛そうです。