KotlinでJSON構築DSLを作る

この記事は[Kotlin Advent Calendar 2014](http://www.adventar.org/calendars/477)の7日目の記事です。
昨日は[@peko_kun](https://twitter.com/peko_kun)さんによる[KotlinでWebをやってみる - Hello Node.kt](http://pekokun.hatenablog.jp/entry/2014/12/06/140536)でした。

## Kotlinとは
 
**[Kotlin (コトリン)](http://kotlinlang.org/)**とは、IntelliJ IDEAなどのIDEで有名な[JetBrains](https://www.jetbrains.com/)社が中心となって開発が進められているプログラミング言語です。JVM言語のひとつで、コンパイラはJavaバイトコードを出力します。いわゆるBetter Javaという位置づけで、Javaから離れすぎないシンプルな文法が特徴的ですが、Javaの悪しき習慣を招いたり、後方互換を維持するために残っているような文法・機能を削除し、Javaにはない強力な機能を有した言語です。JetBrains社はJavaとの完全な相互運用性と、Javaと同等以上のコンパイル速度、実行速度を目指すと言っています。
Kotlinに興味を持たれた方はすぐに試すことができます。[Kotlin Web Demo](http://kotlin-demo.jetbrains.com/)はお使いのWebブラウザ上でKotlinコードを編集、実行ができます。シンタックスハイライトやメソッド名補完などを行ってくれる便利なツールです。
 
## 今回のゴール

今回はKotlinを使ってJSONを構築するためのDSL(内部DSL)を作ってみたいと思います。
具体的には次のJSONを出力したいです。

```json
{
  "articles": [
    {
      "title": "Foo",
      "tags": [1, 2],
      "author": { "id":"111", "name":"hoge" }
    },
    {
      "title": "Bar",
      "tags": [3],
      "author": { "id":"222", "name":"fuga" }
    }
  ]
}
```

そしてこのJSONに対応するKotlinコードを下記のような記述で実現したいです。

```kotlin
jsonObject {
  "articles" to jsonArray {
    +jsonObject {
      "title" to "Foo"
      "tags" to listOf(1, 2)
      "author" to jsonObject {
        "id" to 111
        "name" to "hoge"
      }
    }
    +jsonObject {
      "title" to "Bar"
      "tags" to listOf(3)
      "author" to jsonObject {
        "id" to 222
        "name" to "fuga"
      }
    }
  }
}
```
 
## 使用する言語機能

今回のDSLを作るにあたって使用するKotlinの言語機能のうち、特徴的なものを紹介します。

### 中置呼び出し

中置呼び出しという構文糖衣があります。次の2つのメソッド呼び出しは等価です。

```kotlin
"Kotlin".compareTo("Java")
```

```kotlin
"Kotlin" compareTo "Java"
```

後者では、ドットと引数の括弧が省略されています。 「レシーバ、メソッド、引数1つ」となるようなメソッド呼び出しの場合に限り中置呼び出しによる記述が可能です。

### 拡張関数

拡張関数は既存のクラスにメソッドを追加できる機能、構文です。`String`クラスに`show`メソッド追加する例を示します。

```kotlin
fun String.show() {
  println(this)
}
```

通常の関数、メソッドのように`fun`キーワードを伴って定義します。この拡張関数がスコープにある箇所で`show`は`String`のメソッドのように扱えます。

```kotlin
"Merry Christmas".show() // => Merry Christmas
```

### 演算子オーバロード

Kotlinは演算子オーバロードをサポートしています。複雑さを避けるためかGroovyのように、演算子に対応した名前を持つメソッドを定義することで演算子オーバロードを実現しています。2項演算子の`+`は、ひとつの引数を取る`plus`メソッドに対応する、という具合です。

### 高階関数とラムダ式

Kotlinでは関数オブジェクトを引数として取る高階関数が多く登場します。大抵の場合は、関数オブジェクトをその場で定義するラムダ式[1] が便利です。関数リテラルは必ず`{`と`}`が必要です。

```kotlin
listOf(1, 2, 3).first({
  i -> i % 2 == 0
})
```

上記の例はリストからラムダ式で渡した条件を満たす最初の要素を取得するメソッド`first`を使っています。このようにメソッド(あるいは関数)の引数にラムダ式を渡す場面が多数あるので構文糖衣が用意されています。

```kotlin
listOf(1, 2, 3) first {
  i -> i % 2 == 0
}
```

引数リストの最後が関数を取る場合、ラムダ式を引数リストの括弧の外に追い出して記述できます。さらに上記の例では`first`を中置呼び出ししており、単なる標準ライブラリの関数呼び出しが、まるで組み込み構文のように見えます。

### 拡張関数とラムダ式

言葉で説明するよりコードで説明した方がわかりやすそうなので、まずコードから。

```kotlin
fun createJFrame(setup: JFrame.()->Unit): JFrame {
  val jframe = JFrame()
  jframe.setup()
  return jframe
}
```

`createJFrame`という関数を定義しました。`setup`は引数の名前です。Kotlinは変数の後に型を置くので`JFrame.()->Unit`は`setup`の型です。`引数の型リスト->返値の型`は関数の型です。例えば`(String, Int)->User`は、`String`と`Int`を引数にとって`User`を返す関数と読めます。`setup`は`Unit`を返す関数ですが、`Unit`は意味ある値を持たない型でJavaでいう`void`と同じです。
型が`()->Unit`だけならば「引数なしで値を返さない関数」と解釈できますが、ここでは`JFrame.()->Unit`となっています。この型は「引数なしで値を返さない**JFrameのメソッド**」です。どういうことなのか。。`createJFrame`の使用例を示します。

```kotlin
val jframe = createJFrame {
  this.setTitle("My Swing App")
  this.setSize(200, 200)
  this.setDefaultCloseOperation(EXIT_ON_CLOSE)
}
```

`createJFrame`の引数にラムダ式を渡しているのがわかると思います。そのラムダ式の中で`this`をレシーバとして`setTitle`や`setSize`などのメソッドを呼び出しています。もうおわかりだと思いますが、この`this`、実は`JFrame`インスタンスです。もちろん`this`を省略して`setTitle("My Swing App")`と記述することも可能です。

## 実装

道具が揃いました。実装は非常にシンプルです。
全部作るのは面倒だし、この記事にとってはあまり意味がないので今回は[org.json](http://www.json.org/java/)を使います。

```kotlin
fun jsonObject(setup: JsonObjectBuilder.() -> Unit): JSONObject
        = JsonObjectBuilder().let { it.setup(); it.jsonObject }
 
fun jsonArray(setup: JsonArrayBuilder.() -> Unit): JSONArray
        = JsonArrayBuilder().let { it.setup(); it.jsonArray }
 
class JsonObjectBuilder {
    val jsonObject = JSONObject()
 
    fun <T> String.to(value: T) {
        jsonObject.put(this, value)
    }
}
 
class JsonArrayBuilder {
    val jsonArray = JSONArray()
 
    fun <T> T.plus() {
        jsonArray.put(this)
    }
}
```

これで全部です。よく見ると紹介した機能が使われていることがわかります。
少し補足すると、`JsonObjectBuilder`は`String.to`、`JsonArrayBuilder`は`T.plus`という拡張関数をクラスのメンバとして持っています。これにより、拡張関数のスコープが絞れます。具体的にはそれを定義したクラス内でのみ有効になります(でも`private`ではない)。

```kotlin
jsonObject {
  // この中はJsonObjectBuilderの拡張関数としてのラムダ式
  // ここの to はJsonObjectBuilderで定義した拡張関数String.toを指す
  "key" to "value"
}
```

実装が完了した今、欲しかったJSONをKotlinで表現できるようになりました。

```kotlin
// Kotlinプログラムのエントリポイント
fun main(args: Array<String>) {
  val json: JSONObject = jsonObject {
    "articles" to jsonArray {
      +jsonObject {
        "title" to "Foo"
        "tags" to listOf(1, 2)
        "author" to jsonObject {
          "id" to 111
          "name" to "hoge"
        }
      }
      +jsonObject {
        "title" to "Bar"
        "tags" to listOf(3)
        "author" to jsonObject {
          "id" to 222
          "name" to "fuga"
        }
      }
    }
  }
 
  println(json) // => 欲しかったJSONが出力される
}
```

## 最後に

Kotlin、かわいい名前で簡単に始められそうな雰囲気ですが、強力な機能を持ち表現力豊かな言語です。
この記事で紹介していない機能はまだまだあるので、興味のある方はぜひ公式ドキュメント、または[私のブログ](http://taro.hatenablog.jp/)をご覧ください。
明日は[@yy_yank](https://twitter.com/yy_yank)さん(今季3回目)のポエム的なやつです。

## 注釈
[1] Kotlinの世界ではラムダ式を関数リテラルと呼ぶことが多いです