【Kotlin】 OperatorとInfixでCustom operatorを作る


[Kotlinアドベントカレンダー2015](http://www.adventar.org/calendars/857) 23日目の記事です。
前日は[@laprasDrum](http://qiita.com/laprasDrum)さんの[「kapt の generateStubs と DI ツールとの関係」](http://qiita.com/laprasDrum/items/417b48972ddefdc3b2cd)でした。

# はじめに

CyberAgent2015年度新卒Androidエンジニア [AAkira(github)](https://github.com/AAkira), [@AAkira(twitter)](https://twitter.com/_a_akira)です。
現在、[Kotlinアドベントカレンダー](http://www.adventar.org/calendars/857) の18日目の執筆者[@magie_poohさん](https://twitter.com/magie_pooh)と25日の執筆予定者[@satorufujiwaraさん](https://twitter.com/satorufujiwara)と共に[AmebaFRESH!](https://amebafresh.tv/pc/index.html)という動画サービスをフルKotlin(Dagger等 一部Java)で開発中です。

# 目次

1. Operator
2. Infix
3. 応用例
 - 2行以上のelvis演算子
 - nullableなif-else

# Operator

## Custom operator(Swift)

KotlinはAndroidとの相性がバッチリでAndroidで用いられている事が多く、
iOS用言語Swiftと言語仕様がとても似ているので、お互い比較される事が多いように感じます。
そんなSwiftには[custom operator](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html)という独自に演算子を追加してゴニョゴニョ出来る仕組みがあります。

Swiftではこのように定義することで

```swift
infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
```

 `+-`という独自のoperatorを用いて便利な計算をすることが出来ます。

```swift
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)

```

他にも`Σ`とか`∫`等の文字も使えるので数学関係の利用にも適してそうです。
イケてるライブラリとかでよく独自の記号が使われていてカッコいいです！

## Operator overloading(Kotilin)

さて本題のKotlinのOperatorについて見ていきましょう
Kotlinでは、前述のSwiftのように好きな__記号__を定義することは出来ないのですが、(後述あり)
C++のようにOperatorをoverloadする事が出来ます。

例えば、普段何気なく使ってるString同士の足し算。よく使いますよね。
Kotlinには[String Templates](https://kotlinlang.org/docs/reference/basic-types.html#string-templates)があるので使わなくてもいいのですが、個人的に見難いのとJavaからの流れでちょっとLog出すだけの時等は`+`で結合してしまいます。
これがどのように実装されているのか、`kotlin.String.kt`のソースを見ると`plus`Operatorをoverloadしているのがわかります。

```kotlin:String.kt
public class String : Comparable<String>, CharSequence {
    ...
    
    public operator fun plus(other: Any?): String
   
    ...
}
```

これによって

```kotlin
val hoge: String = "foo" + "bar"
```
みたいな事が実現出来ます。
plusをoverload出来るということは引き算や足し算、Rangeや比較演算子も書けます。[[Kotlin Expression一覧]](https://kotlinlang.org/docs/reference/operator-overloading.html)
拡張関数と組み合わせるといい感じに書けます。~~`toInt()`が微妙ですが…~~

```kotlin
operator fun String.minus(other: String) = this.toInt().minus(other.toInt())

operator fun String.times(other: String) = this.toInt().times(other.toInt())

val foo = "10" - "5" // 5
val bar = "10" * "5" // 50
```

ちなみにKotlinの拡張関数は[必ずメンバのメソッドが呼び出される](https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically)ため、既に親クラスに定義されているoperatorを拡張関数で書き換えてもコンパイルエラーにはなりませんが値は変わりません。
拡張関数は何処にでも書けてしまい、優先順位が付けられないので、当然と言えば当然の挙動ですね。

```kotlin
operator fun Int.plus(other: Int) = this.minus(other)

val hoge = 10 + 5 // 15
```

## 利用シーン

~~[ThreeTen](http://www.threeten.org)使えって話ですが、~~`Comparable`を実装した日付を表すclassがあったとします。

```kotlin:MyDate.kt
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate) = when {
        year != other.year -> year - other.year
        month != other.month -> month - other.month
        else -> dayOfMonth - other.dayOfMonth
    }
}
```
さらに`contains`operatorを持った日付の範囲を計算する`DateRange`classを作成し、拡張関数で`MyDate`classに`rangeTo`operatorを足します。

```kotlin
class DateRange(val start: MyDate, val endInclusive: MyDate) {
    operator fun contains(item: MyDate) = start <= item && item <= endInclusive
}

operator fun MyDate.rangeTo(other: MyDate): DateRange = DateRange(this, other)

val date = MyDate(2005, 5, 19)
val first = MyDate(1977, 5, 25)
val end = MyDate(2015, 12, 18)

val hoge = date in first..last // true
```
このようにOperatorを独自のclassに足すことが出来るので、
Javaでは出来なかった様々な事が行えるようになりました！
便利ですねKotlin(\*´ω｀*)


# Infix

Kotlinには上記のOperatorと似た機能として[Infix](https://kotlinlang.org/docs/reference/functions.html#infix-notation)というものがあります。
上記でのOperatorの説明ではSwiftのように好きな__記号__を定義することは出来ないと書いたのですが、
なんとKotlinはメソッド名で使える文字列に関してはCustom Operatorを作ることが出来ます！！

###(12/21 11:30補足)
---
記号は定義出来ないと書いたのですが、たろうさんに[アクサン グラーブを使うと書けなくは無い](https://twitter.com/ngsw_taro/status/678758565251710977)と教えていただきました！
> infix fun Int.\`\*\*\`(n: Int) = ...
5 \`**` 3 //=> 125

ただ、本来はJavaコード上での識別子がKotlinの予約語とぶつかったときの対処法らしいので本来避けるべき書き方みたいです。

---


例えばBooleanはこの様に書いても比較することが出来ます。

```kotlin
val foo = true
val bar = true

// pattern 1
if(foo and bar) return

// pattern 2
val hoge = when {
    foo or bar -> 100
    else -> 0
}
```

Booleanのソースを見ると、このように定義されています。

```kotlin:Boolean.kt
public class Boolean private () : Comparable<Boolean> {
    /**
     * Returns the inverse of this boolean.
     */
    public operator fun not(): Boolean

    /**
     * Performs a logical `and` operation between this Boolean and the [other] one.
     */
    public infix fun and(other: Boolean): Boolean

    /**
     * Performs a logical `or` operation between this Boolean and the [other] one.
     */
    public infix fun or(other: Boolean): Boolean

    /**
     * Performs a logical `xor` operation between this Boolean and the [other] one.
     */
    public infix fun xor(other: Boolean): Boolean

    ...
}
```

先程の`operator`と`infix`が定義されてますね。
つまり、Kotlinでも`infix`を利用すればcustom operatorが作れるということです。

# 応用例

## 2行以上のElvis演算子

先日[kotlinlang-jpのslack](http://kotlinlang-jp.herokuapp.com)で
null checkは「`if else`」でするか、「Elvis演算子」でするかが話題になっていたのですが、
個人的にKotlinでは、比較にnullを使いたく無いと思っています。(代入や戻り値以外でnullを書きたくない)
そのため普段コードを書く時には

```kotlin
val foo: Int? = null
foo = 100

val hoge = foo ?: 0
```

のようにして代入を行ったり

```kotlin
fun hoge(value: Int?) {
    valure ?: return
    ...
}
```
のようにして早期returnをおこなっているのですが、エルビス演算子は後ろに1行しかとれない問題があります。
現状チームとしては`run`を使って2行以上の処理を行っています。

```kotlin
fun generator() = 10 * 10
val foo: Int? = null
foo = 100

// possible
val hoge = foo ?: 0

// possible
val hoge = foo ?: generator()

// possible
val hoge = foo ?: run {
    // 2行以上の処理
}

// impossible
val bar = true
val hoge = foo ?: {
   // 2行以上の処理
   val a = if(bar) 10 else 100
   a * generator()
}
```

そこで、`infix`を用いて以下の様な関数を作成します。

```kotlin
inline infix fun <R> R?.elvis(f: () -> R): R = this ?: f()
```

これを用いると先ほど書けなかった式のコンパイルが通ります。(名前はもう少し考えたいですね…）

```kotlin
// possible :-D
val hoge = foo elvis {
   // 2行以上の処理
   val a = if(bar) 10 else 100
   a * generator()
}
```

## nullableなif-else

他に使えそうな箇所としては、
ifで比較をしたい時に中身がnullableだとKotlinのコンパイラはエラーを出します。
nullだったらfalseの方にして欲しいけど、比較前にsmart castして…と結構手間なことがあります。
そんな時に先ほど自作した`elvis`関数と`infix`を組み合わせて このように書けます。

```kotlin
val hoge: Int? = null

inline infix fun <R> nullableIf(value: Boolean?, f: () -> R): R? {
    value ?: return null
    return if (value) f() else null
}

nullableIf(hoge == 0) {
    // don't be called here
} elvis {
    // be called here
}

```

__凄いぞKotlin!!__
なんだかんだKotlin歴6ヶ月になりますが、この記事を書いてる時に初めてこんな事が出来る！！って気づきました。
Kotlinには`requireNotNull()`等の便利なメソッドが標準で多数用意されているので、
もしかするとこんな事をしないでもいい感じに実現出来るのかもしれないですが、
他の用途にも応用出来ると思うので面白い使い方があったら教えて下さい！

以上`operator`と`infix`についてお話をしました。
何か間違い等ありましたら、[@AAkira](https://twitter.com/_a_akira), [Mail](developer.a.akira@gmail.com)に連絡を頂けると有り難いです。
Slackにもいるので是非みなさんで議論しましょう！


---
明日はrabitarochanさんです。