Kotlinのstdlibのgeneratedについて

# stdlibと拡張関数とコレクション

Kotlinのstdlibはほぼ拡張関数の集まりなのですが、Javaにはプリミティブ型があり、それらは型パラメータとして扱えないため、コレクションに対して拡張関数を書くには以下のように型パラメータ、byte、short、int、long、float、double、boolean、charに対して似たようなコードを繰り返し書かなければなりません。

```kotlin
/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun <T> Array<out T>.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun ByteArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun ShortArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun IntArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun LongArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun FloatArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun DoubleArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun BooleanArray.isEmpty(): Boolean {
    return size == 0
}

/**
 * Returns `true` if the array is empty.
 */
@kotlin.internal.InlineOnly
public inline fun CharArray.isEmpty(): Boolean {
    return size == 0
}
```

# stdlibのコード生成

そこで、Kotlinのstdlibではコレクションに対する拡張関数をテンプレートからコード生成しており、生成されたコードは `libraries/stdlib/src/genereted/` に置かれます。

<img width="180" alt="Screen Shot 2016-08-27 at 12.32.30 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/179/44db0a03-5919-6604-8f57-edf93ebe9c3b.png">

例えば上の `isEmpty` のテンプレートは `libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt` に以下のように定義されています。

```kotlin
templates add f("isEmpty()") {
    inline(Inline.Only)
    only(ArraysOfObjects, ArraysOfPrimitives)
    doc { "Returns `true` if the array is empty." }
    returns("Boolean")
    body {
        "return size == 0"
    }
}
```

少しトリッキーに見えますが `f` の定義を見てみると

```kotlin
fun f(signature: String, init: GenericFunction.() -> Unit) = GenericFunction(signature).apply(init)
```

となっており、ブロックのレシーバーが `GenericFunction` で、そのクラスがメソッドのビルダーになっていることが分かります。

テンプレートからコードを生成するには以下のコマンドを実行します。

```
$ cd libraries/tools/kotlin-stdlib-gen
$ mvn compile exec:java
```

これで `libraries/stdlib/src/generated/Arrays.kt` に `isEmpty` が生成されます。

# stdlibのコレクションに拡張関数を追加する

実際に `peek` という、デバッグのためにリストの中身を覗き見るためのメソッドを追加してみます。

```kotlin
listOf(1, 2, 3)
        .map { ... }
        .peek { println(it) } //=> 1, 2, 3
        .map { ... }
```

ビルダーのメソッドの一部を紹介します。

name | what
:---|:---
inline | 生成されるメソッドをインラインにするかを指定します
typeParam | 型パラメータを指定します
only | どのコレクション（Familyと呼ばれています）に対して拡張関数を定義するかを指定します
returns | 返り値の型を指定します
body | メソッドの中身はここに書きます

雑な `peek` の実装は以下のようになります。

```kotlin
templates add f("peek(action: (T) -> Unit)") {
    inline(true)
    only(Lists)
    typeParam("T")
    returns("List<T>")
    body {
        """
            for (item in this)
                action.invoke(item)
            return this
        """
    }
}
```

テンプレートの定義は独自記法で少しクセがあるので先に `generated` の方に実装したいメソッドを書いてみて、動いたらテンプレートに移すのが良いと思われます。

書けたらビルドして実行してみます。

```
$ mvn compile exec:java
$ ant runtime
$ dist/kotlinc/bin/kotlinc
Type :help for help, :quit for quit
>>> listOf(1,2,3).peek { println(it) }
1
2
3
[1, 2, 3]
```

# Familyについて

`Family` はどのクラスに対して生成するのか対応になっています。

`templates` の `only` で `Family` 指定します。デフォルトは全てのFamilyに対して生成するようになっており、対応は以下の通りです。

Family | packageName | SourceFile
:---|:---|:---
Iterables | "kotlin.collections" | Collections
Collections | "kotlin.collections" | Collections
Lists | "kotlin.collections" | Collections
Sets | "kotlin.collections" | Sets
Maps | "kotlin.collections" | Maps
InvariantArraysOfObjects | "kotlin.collections" | Arrays
ArraysOfObjects | "kotlin.collections" | Arrays
ArraysOfPrimitives | "kotlin.collections" | Arrays
Sequences | "kotlin.sequences" | Sequences
CharSequences | "kotlin.text" | Strings
Strings | "kotlin.text" | Strings
Ranges | "kotlin.ranges" | Ranges
RangesOfPrimitives | "kotlin.ranges" | Ranges
ProgressionsOfPrimitives | "kotlin.ranges" | Ranges
Generic | "" | Misc
Primitives | "" | Misc

`peek` の例では `only` に　`Lists` を指定したので `libraries/stdlib/src/generated/_Collections.kt` にメソッドが生成された、という感じです。