以下の記事を読んだ。
この記事の要点をかいつまんで話すと、以下の通りになる:
- JSONの代替として使われているYAMLはオーバースペックではないか
- キーのスキーマが不安定だったりする
- 求めているものは、コメントが書けるJSONくらいのものなのではないか
- いま使われているからそれを使うということになっているのではないか
- とりあえず各言語にライブラリがあるという点ではYAMLが利用されがち
だいたい同意する。また、個人的にYAMLは好きではなかったので、納得いったという側面もある。
予想外の振舞いが多いというのは、オーバースペックをほのめかす分かりやすい兆候だと考えている。
いくつか考えたことをまとめておく。
- JSONの問題
- そもそもYAMLやJSONが本当に必要か
- シリアライズフォーマットの対立軸
- Windymeltがちょっとした設定ファイルや構造化された入力を与えるときどうしているか
- includeの仕組みを本当に設定ファイルに寄せるべきなのか
- 全体を通した感想
JSONの問題
YAMLは小回りがききにくい一方で、JSONにも問題がある。例えば数値の扱いである。
JavaScriptのNumber型について、Moazillaは以下のように説明している:
JavaScript の Number 型は IEEE 754 の倍精度 64ビットバイナリー形式であり、 Java や C# の double のようなものです。
JSONだと、どうか。JSONの規格はRFC8259が標準的な地位にある。
RFCによれば、JSONの数値型は記法についての規定があるばかりで、桁数などの制限は無い。つまりシンタックスは定められているがセマンティクスは委ねられているという状態にある。IEEE 754 binary64を想定しておけば十分、と仕様書にはある。自分は大きな数値をJSONに含めた結果、JS側の処理系が数値を丸めてしまいデータの交換に失敗した経験があるので、数字の型はシリアライズフォーマットがセマンティクスまでカバーしてほしいと考えている。
他にも、帯に短しといった感じの問題がいくつかある。
そもそもYAMLやJSONが本当に必要か
設定ファイルを書く目的でシリアライズフォーマットを使う場合、そもそもYAMLやJSONでなければならないのか考えたほうがよい。オープンにユーザが設定ファイルを書き換えられるのであればともかく、開発者だけが自分たちのプロダクトの設定を一箇所に集約させたい、というだけであれば、その言語の表現で書いたほうが良い。他のコンポーネントからも設定を参照したいということになればシリアライズフォーマットの必要性が生じてくる(が、一般論として、そもそも他のコンポーネントから別のコンポーネントの設定を参照するべきではない)。
ユーザが設定することを目的として設定ファイルをシリアライズ・デシリアライズしたいのであれば、もちろんJSONやYAMLを使うことになるだろう(例: Docker Composeの設定)。
シリアライズフォーマットの対立軸
いくつか存在するシリアライズフォーマットについて考えるために、いくつかの対立軸を作ってみる:
- コメントが書けるかそうではないか
- マークアップかそうではないか
- スキーマが存在するか
- オフサイドルールの存在
- 他の設定ファイルをincludeできるか
- データ型は厳密か
- 厳密であるほど、「強い」シリアライズが行われているといえる
- 1つの表現に対して唯一の表現が定まるか
- Twitterのフォロワーのコメントを見て気付いた: https://twitter.com/aumy_f/status/1588874063401062400
- より強くこれを要求すると、連想配列はアルファベット順にする、といった制約が発生する
Windymeltがちょっとした設定ファイルや構造化された入力を与えるときどうしているか
じゃあ自分が普段からどうして暮らしているか紹介する。
全体としてはJSONが多い。個人的にScalaをよく書くのだが、JSONライブラリは豊富に用意されている(もちろんYAMLのライブラリなども豊富に存在する)。
プログラムに与える設定ファイルとしてはHOCONをよく使う。Java / Scala界ではかなり使われている印象があるが、他の界隈では全く見ない。シリアライズフォーマットというよりは、より高級なレイヤを内包したコンフィギュレーション言語という感じだ:
そして、プログラムに与える入力としてはJSON Linesを良く使っている。JSON Linesとは、1行に1JSON Objectを書いた表現。
コメントは、comment
フィールドをoptionalで与えることで、好きなコメントを書けるようにしている。欠点は、それを読んで書き戻したとき、コメントも戻さなければならないのであれば、それはコメントが意味を持っていることになってしまうので、本当の意味のコメントではないところ。
includeの仕組みを本当に設定ファイルに寄せるべきなのか
YAMLの実装の一部などにはincludeの仕組みがあるけれど、これを本当に設定ファイルでやるの?と思っている。個人的には、コード側の責務として実装したほうが良いと思っている。
全体を通した感想
そもそもコメントはシンタックス(書き方)の範疇なのか、セマンティック(振舞い)の範疇なのか、という問題がありそう。個人的には、シンタックスでコメントをサポートしてほしい。
また、用途によって適切なシリアライズ形式は違うということもあらためて確認しておきたい。設定ファイルやネットワーク越しのデータ交換、ローカルへのデータ保存など、ユースケースによって適切なデータフォーマットは異なる。
データ型はシリアライズフォーマットに内包されていてほしい。つまり、処理系による選択の余地をなるだけ減らしてほしい。intとfloatは分離してほしい。今のところECMAScript / nodeが大きな存在感を放っている以上、難しいのだろうけれど。
そして最後に、人間が読み書きする場合はいくつかの構文上のサポートがないと苦しいものがあるという点を強調したい:
- オフサイドルール
- コメント
- 連想配列のキーのdouble quotationを特定の場合省略していい機能
/[0-9a-zA-Z_]+/
であるような場合は省略されたい
- 複数行文字列リテラル
そこで、何か別の形式で書いて、それを中間的にJSONに変換するというのは?という提案をしたい。 ECMAScriptに対して我々がTypeSctiptでしたように、JSONへとトランスパイルされる言語を作ればよいのではないか。 YAMLよりもシンプルな最小限の感じで。既にあるかも?