Kotlinのstdlibのコードのうちいくつかは自動生成されている

この投稿の内容および参照先のコードは、Kotlin 1.3.20です。

# よく使うあの関数、自動生成されたコードで記述されている

Kotlinの各種コレクションの拡張関数である[map](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html)や[filter](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter.html)などはstdlib内に定義されています。

* [Iterable#mapのコード](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/stdlib/common/src/generated/_Collections.kt#L1280-L1286)
* [Iterable#filterのコード](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/stdlib/common/src/generated/_Collections.kt#L663-L668)

さてこの拡張関数が定義されているコードの[先頭部分](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/stdlib/common/src/generated/_Collections.kt#L11-L14)には次のようなことが記載されています。

```
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
```

どうやら[map](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html)や[filter](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter.html)が定義されているコードは、自動生成されているようです。

上記に記述のある通り、[libraries/stdlib](https://github.com/JetBrains/kotlin/tree/1.3.20/libraries/stdlib)を参照してみましょう。ここのReadMeは次のようになっています。

![スクリーンショット 2018-11-28 17.00.53.png](https://qiita-image-store.s3.amazonaws.com/0/4125/0b08a1ac-1a4f-c55b-c1c2-406f92b7fdd8.png)

やはり自動生成が行われているようです。こちらの一文に注目してください。

> This then runs the script which generates a significant part of stdlib sources from the templates authored with a special kotlin based DSL.

自動生成のコードはKotlinのDSLとなっています。この投稿の残りでは、そのDSLを覗いてみましょう。

# stdlibの自動生成のDSLをのぞいてみよう

stdlibのコードの自動生成を行なうDSLは、以下の場所にあります。

https://github.com/JetBrains/kotlin/tree/1.3.20/libraries/tools/kotlin-stdlib-gen

## エントリーポイントは？

まずはbuild.gradleをみてみましょう。

https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/build.gradle

ここの内容から、エントリーポイントは[generators.GenerateStandardLibKt](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt)であることがわかりました。


次はこの[generators.GenerateStandardLibKt](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt)をみてみましょう。

[templatesパッケージ](https://github.com/JetBrains/kotlin/tree/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates)には、次のような[TemplateGroup](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/TemplateGroups.kt)のサブクラスが定義されており、それを列挙しています。

```kotlin
    val templateGroups = sequenceOf<TemplateGroup>(
        Elements,
        Filtering,
        Ordering,
        ArrayOps,
        Snapshots,
        Mapping,
        SetOps,
        Aggregates,
        Guards,
        Generators,
        StringJoinOps,
        SequenceOps,
        RangeOps,
        Numeric,
        ComparableOps
    )
```

このFilteringやMapping、SnapshotsなどはDSLを活用して記述されています。


下記のコードで、プラットフォーム別の目的のディレクトリにアウトプットします。

```kotlin
    templateGroups.groupByFileAndWrite { (platform, source) ->
        //        File("build/out/$platform/$source.kt")
        when (platform) {
            Platform.Common -> commonDir.resolve("_${source.name.capitalize()}.kt")
            Platform.JVM -> jvmDir.resolve("_${source.name.capitalize()}Jvm.kt")
            Platform.JS -> jsDir.resolve("_${source.name.capitalize()}Js.kt")
            Platform.Native -> error("Native is unsupported yet")
        }
    }
```


## DSLで記述された部分は？

先ほども出てきた次の[libraries/tools/kotlin-stdlib-gen/src/template](https://github.com/JetBrains/kotlin/tree/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates)下にあるクラスは、DSLを用いて記述されています。

* Elements
* Filtering
* Ordering
* ArrayOps
* Snapshots
* Mapping
* SetOps
* Aggregates
* Guards
* Generators
* StringJoinOps
* SequenceOps
* RangeOps
* Numeric
* ComparableOps

それぞれの内容は、関数の系統ごとクラスごとにまとめられています。

たとえば、

* Mappingは、mapやmapNotNullなどの射影とそれに近い処理を行う関数
* Filteringは、filterやtakeWhileなどの選択とそれに近い処理を行う関数
* SetOps（ファイル名はSets、クラス名はSetOps）は、unionやintersectなどの集合演算を行う関数

がまとめられています。

各種コレクション型の[filter関数を生成する部分](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt#L524-L549)をみてみましょう。

```kotlin
    val f_filter = fn("filter(predicate: (T) -> Boolean)") {
        includeDefault()
        include(CharSequences, Strings)
    } builder {
        inline()

        doc { "Returns a ${f.mapResult} containing only ${f.element.pluralize()} matching the given [predicate]." }
        returns("List<T>")
        body {
            """
            return filterTo(ArrayList<T>(), predicate)
            """
        }

        specialFor(Strings, CharSequences) {
            returns("SELF")
            doc { "Returns a ${f.collection} containing only those characters from the original ${f.collection} that match the given [predicate]." }
            body { """return filterTo(StringBuilder(), predicate)${toResult(f)}""" }
        }

        specialFor(Sequences) {
            inline(Inline.No)
            returns("Sequence<T>")
            body { """return FilteringSequence(this, true, predicate)""" }
        }
    }
```

このようにKotlinのDSLを活用して記述されています。

上記のコード中のdocはコメント部を、bodyはメソッドの実装の生成を担っています。

次のコメントを担っている部分に注目してください。Kotlinの言語機能を活用していますね。

```kotlin
doc { "Returns a ${f.mapResult} containing only ${f.element.pluralize()} matching the given [predicate]." }
```

また、Sequenceに対してfilterメソッドは、inline化を行わないなどの特殊化をする必要があります。そこで上記のコードのように`specialFor`を使って特殊化をしています。

```
specialFor(Sequences) {
    inline(Inline.No)
    returns("Sequence<T>")
    body { """return FilteringSequence(this, true, predicate)""" }
}
```

これらのDSLで記述したコードは、どのような効果があるか簡単に想像できるのではないでしょうか？

## DSLの定義は？

KotlinのDSLについては以下の公式ドキュメントを参照してください。

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/index.html
https://kotlinlang.org/docs/reference/type-safe-builders.html
https://kotlinlang.org/docs/reference/type-safe-builders.html#scope-control-dslmarker-since-11

特に、Kotlin 1.1から利用可能な`@DslMaker`に注目してください。

stdlibのコードジェネレーターDSLの実装は、[libraries/tools/kotlin-stdlib-gen/src/templates/dsl](https://github.com/JetBrains/kotlin/tree/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl)にあるコード群です。

`@DskMaker`を活用しているのが、[TemplateDsl](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt#L9)アノテーションです。

それを活用しているのが次のクラス群です。

* [FamilyPrimitiveMemberDefinition](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt#L155-L197)
* [PairPrimitiveMemberDefinition](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/Templates.kt#L199-L217)
* [MemberBuilder](https://github.com/JetBrains/kotlin/blob/1.3.20/libraries/tools/kotlin-stdlib-gen/src/templates/dsl/MemberBuilder.kt#L25)

# まとめ

Kotlinでコーディングをしているときによく使うmapやfilterという関数のコードは、実は自動生成されています。

そしてその自動生成のコードはKotlinの言語機能を活用したDSLで実装されています。

もしあなたが、KotlinでDSLを作ることになったときにもしかしたら参考になるかもしれません。ぜひ覗いてみてください。

# 関連スライド

* [Kotlinのリストのmap関数、自動生成されているって！](https:/eakerdeck.com/ryotamurohoshi/kotlinfalserisutofalsemapguan-shu-zi-dong-sheng-cheng-sareteirutute)