kotlinのT.() -> Unitや<T>を理解する


kotlinの特徴の一つにスコープ関数がありますが、例えば`let`ですとラムダの中で`it`で受けたり、`run`だと`this`で受けたりしています。どこからこの差異が来るのか不思議になり、実装箇所である[Standard.kt](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt)を見ていたら、`T.() -> Unit`やら`<T>`やらどこかで出会っていながらすんなりと頭に入ってこない表記があったので、自分の中での整理のために考えてみました。
間違っている場合はご指摘いただきますと幸いです。

```kotlin:apply
public inline fun <T> T.apply(block: T.() -> Unit): T
```
```kotlin:also
public inline fun <T> T.also(block: (T) -> Unit): T 
```

## `T.() -> Unit`について

`T.() -> Unit`はどういう意味かを考えるために、より具体的にイメージしやすいように、レシーバと引数の二乗和を返す関数`sumOfSquares`を実装してみました。

```kotlin
val sumOfSquares: Int.(Int) -> Int = { other -> (this * this).plus(other * other) }
10.sumOfSquares(4) //=> 116
```
変数`sumOfSquares`の型は関数型で`Int.(Int) -> Int`は

1. レシーバがInt型（一つ目のInt）
2. 引数がInt型（二つ目のInt）
3. 返り値がInt型（三つ目のInt）

を意味します。大まかには以下と同じです。

```kotlin
fun Int.sumOfSquares(other : Int) = (this * this).plus(other * other)
```
つまり、`apply`の引数定義にある`T.() -> Unit`は

1. レシーバがT型
2. 引数なし
3. 返り値なし

を意味します。`apply`はこの三つの条件を満たす関数リテラルを引数としてとるというわけですね。

## 関数名の前の`<T>`について
次に関数名の前についている`<T>`ですが、これはジェネリクスを実装する際の書き方になります。なかなか自分でジェネリクスを実装する機会は無いかもしれませんが、ジェネリクスを実装するときは型パラメーターは関数名の前に置きます（[参照](https://kotlinlang.org/docs/reference/generics.html#generic-functions)）。そして、呼び出すときは、関数名の後に型パラメータを置きます。

簡単な例として、オブジェクトを文字列で返す`inspect`という関数をジェネリクスで実装してみました。ジェネリクスの実装には、型パラメータを型引数として与えて呼び出す方法と、型パラメータをレシーバとして用いる方法の二つがあります。

- 型パラメータを型引数として用いる例

```kotlin
// サンプルのためのデータクラスでUserを作っておきます。
data class User(var name : String)

fun <T> inspect(item : T): String {
    return "$item"
}

inspect<String>("Jiro") //=> Jiro
inspect<User>(User("Jiro")) //=> User(name=Jiro)
```
上記の例だと型パラメータを型引数として与え、さらに通常の引数を記述することになります。
これは少し面倒です。そこで、オブジェクトそのものに`inspect()`というメソッドを生やすことができます。その場合は、拡張関数にする必要があります。

- 型パラメータをメソッドのレシーバとして用いる例

```kotlin
fun <T> T.inspect(): String {
    return "$this"
}

"Jiro".inspect() //=> Jiro
User("Jiro").inspect() //=> User(name=Jiro)
null.inspect() //=> null
```
以上のことを踏まえて、`apply`と`also`をみてみます。

## [apply](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt#L67)の場合
```kotlin:apply
public inline fun <T> T.apply(block: T.() -> Unit): T
```
1. Tという型のオブジェクトに`apply`というメソッドを生やす。
    2. ここで、TはAny?扱いなので、全てのオブジェクトにメソッドが生えることになる。
2. 返り値のオブジェクトの型はT（コードを見ると最後に`return this`している。ここでの`this`はレシーバを指す。）
3. 引数は関数リテラルで、
    4. 引数は、Tという型のオブジェクトの関数で<strong>引数は無い</strong>関数リテラル
    5. 返り値はない（Unit）

ということを意味します。例えば使用例は以下のようになりますが、

```kotlin:kotlin
// サンプルのためにPersonクラスを簡易的に実装しています
class Person() {
    var name : String = ""
}

val user = Person().apply {
    name = "Jiro"
}
```
確かにPerson型のオブジェクトにapplyメソッドが生えており、引数なし・返り値なしの関数リテラルをapplyの引数としてとっています。
ちなみに余談ですが、kotlinの場合、引数の最後のラムダ式を取る場合、括弧を省略できるので上記のような記述ができます。このような書き方ができることがkotlinでDSLを作りやすくしているわけですね。このあたりはrubyとの近しい印象を受けます。

```kotlin:括弧があっても良い
val user = Person().apply({
    name = "Jiro"
})
```

## [also](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt#L79)の場合
```kotlin:also
public inline fun <T> T.also(block: (T) -> Unit): T 
```
1. Tという型のオブジェクトに`also`というメソッドを生やす。
    2. ここで、TはAny?扱いなので、全てのオブジェクトにメソッドが生えることになる。
2. 返り値のオブジェクトの型はT（コードを見ると最後に`return this`している。ここでの`this`はレシーバを指す。）
3. 引数は関数リテラルで、
    4. 引数は、<strong>Tという型のオブジェクトを引数に持つ</strong>関数リテラル
    5. 返り値はない（Unit）

```kotlin
val user = User().also { user ->
    user.name = "Jiro"
}
```
このとき`also`の引数の関数リテラル内での`this`は`user`ではなく外側のものと同じになります。

# 参考
 - [Generic functions](https://kotlinlang.org/docs/reference/generics.html#generic-functions)
 - [Extensions](https://kotlinlang.org/docs/reference/extensions.html)
 - [Higher-Order Functions and Lambdas](https://kotlinlang.org/docs/reference/lambdas.html)