JSON から各言語のコードを生成する quicktype の Haskell 出力を実装した

## 概要

quicktype の Haskell 出力を実装しました。
https://github.com/quicktype/quicktype
quicktype の機能と使い方を紹介します。

## quicktype とは

quicktype は JSON などのデータを入力として、各プログラミング言語から JSON を呼び出すコードとそのデータに対応する強い型付きのモデルを自動的に生成してくれるライブラリです。
ライブラリ本体は TypeScript で書かれています。
データからモデルを正しく生成するのは地味で面倒な単純作業が必要ですが、その手間を大幅に削減してくれます。

### 入力データ

JSON 以外の形式も入力データに使うことができます。

- JSON
- JSON Schema
- TypeScript
- GraphQL query

### 出力言語

Haskell の出力を今回実装しました。それ以外にも以下の言語やフォーマットに出力できます。

- Haskell (New!)
- Ruby
- JavaScript
- Flow
- Rust
- Kotlin
- Dart
- Python
- C#
- Go
- C++
- Java
- TypeScript
- Swift
- Objective-C
- Elm
- JSON Schema
- Pike
- Prop-Types

## 使い方

ここでは Web UI と CLI による2つの実行方法をそれぞれ紹介します。

### Web UI

Web で quicktype の機能を試すことができます。
https://app.quicktype.io/

たとえば JSON データから Haskell のコードを生成したいとします。

1. データ型の指定  
画面左の "Source Type" に "JSON" を指定して、"Name" にトップレベルの名前を入力します。
2. データの入力  
JSON データを画面左下の項目に入力します。
3. 出力フォーマットの指定  
画面上のメニューで "Options" をクリックすると、右側に表示されるモーダルで "Language" で "Haskell" を選択します。
エラーがなければ画面右に Haskell のコードが生成されます。

入力データ (JSON)

```json
{
  "greeting": "Welcome to quicktype!",
  "instructions": [
    "Type or paste JSON here",
    "Or choose a sample above",
    "quicktype will generate code in your",
    "chosen language to parse the sample data"
  ]
}
```

出力コード (Haskell)

```hs
{-# LANGUAGE StrictData #-}
{-# LANGUAGE OverloadedStrings #-}

module QuickType
    ( Welcome (..)
    , decodeTopLevel
    ) where

import Data.Aeson
import Data.Aeson.Types (emptyObject)
import Data.ByteString.Lazy (ByteString)
import Data.HashMap.Strict (HashMap)
import Data.Text (Text)
import Data.Vector (Vector)

data Welcome = Welcome
    { greetingWelcome :: Text
    , instructionsWelcome :: Vector Text
    } deriving (Show)

decodeTopLevel :: ByteString -> Maybe Welcome
decodeTopLevel = decode

instance ToJSON Welcome where
    toJSON (Welcome greetingWelcome instructionsWelcome) =
        object
        [ "greeting" .= greetingWelcome
        , "instructions" .= instructionsWelcome
        ]

instance FromJSON Welcome where
    parseJSON (Object v) = Welcome
        <$> v .: "greeting"
        <*> v .: "instructions"

```

### CLI

コマンドラインでも quicktype を実行できます。
実行環境に [node.js](https://nodejs.org/) がインストールされているものとします。

1. インストール  
`npm install -g quicktype`
2. 実行  
`echo '{ "hello": "Haskell" }' | quicktype -l haskell`

オプション `-l` で出力言語を指定します。
ファイル入力や JSON Schema (オプション `-s`) による型の定義もできます。
詳しい使い方やその他のCLIオプションについては公式リポジトリの[ドキュメント](https://github.com/quicktype/quicktype#using-quicktype)を参照してください。

## Haskell 版の実装の特徴

- Enum, Union, GraphQL にも対応した  
他の一部の言語では対応していません。
- [Aeson](https://hackage.haskell.org/package/aeson) を使って JSON に対応した  
個人的によく使っているライブラリなので使うことにしました。型安全なモデルを作るという quicktype の思想にもあっているはずです。
- [Lens Aeson](https://hackage.haskell.org/package/lens-aeson) は使わなかった  
Template Haskell を使って簡易に実装する方法も検討しましたが、例外的なデータに柔軟な対応ができなかったので今回は使いませんでした。もし需要があれば後から付け足すかもしれません。
- [StrictData言語拡張](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#extension-StrictData)を使った  
遅延評価によるパフォーマンス低下を防ぐために入れました(全部のフィールドに "!" をつけるのが面倒だった)

## コメント

- Haskell 版は最初のバージョンで荒削りなので一部の例外的なデータ型を正しく出力できないことがあります。実装に対する疑問や改善は Issue や PR にして公式リポジトリに送ってください。レスポンスが早いのですぐに対応してくれると思います。