http://twitter.com/hiyama_on_caty でゴニョゴニョ言っていた(http://twitter.com/hiyama_on_caty/status/26596332016 の前後)の件を解説してみます。
Catyのデータ型自動変換のメカニズム
次のようなデータ型を考えます。
データ型定義の意味は見ればわかるとは思いますが、構文はCatyスキーマ言語(「最近のCatyスキーマを解説します」)です。列挙型として、リテラルのユニオン型を使っています。
/** ピザの注文のデータ型 */type PizzaOrder = {
// ベースタイプは必須
"baseType" : ( "プレーン"
| "マルゲリータ"
| "マリラーナ"
| "ロマーナ"
| "ナポリターナ"
),
// トッピングのデフォルト値は [](空列)
"topping" : [
( "オニオン"
| "ズッキーニ"
| "トマト"
| "ブラックオリーブ"
| "マッシュルーム"
| "コーン"
| "エビ"
| "ブロッコリー"
| "ベーコン"
| "モッツァレラチーズ"
| "ガーリック"
| "バジル"
)* ]?,
// 数量のデフォルト値は 1
"quantity" : integer(minimum = 1)?
};
Catyで何かしようとしたら、どんなときでも、まずは型定義が必要です。スキーマドリブンなんです。
さて、普通のHTMLフォームからピザの注文が来るとして、そのデータは次のようなものでしょう。
baseType=マルゲリータ&topping=ベーコン&quantity=2
CatyのWebアダプターは、これを次のように変換します。
{
"baseType" : ["マルゲリータ"],
"topping" : ["ベーコン"],
"quantity": ["2"]
}
つまり、Webからの入り口のプロセッサが application/x-www-form-urlencoded から application/json への変換を勝手にやります。Catyの本体であるスクリプト・インタプリタに入ってきたときには単なるJSONデータで、それがどこから来たかはもう分かりません。したがって、同じデータをキーボードやファイルから渡してもまったく同じ挙動を示します。
オブジェクトのプロパティ値が配列になっているのが不思議かも知れませんが、WebアダプターはCatyコアの外にいるので、Caty型システムのデータ型は知りません。Webアダプターができることは、型のセマンティクスを考慮しない機械的な変換だけです。application/x-www-form-urlencoded はマルチバリュー(同じ名前の複数の名前・値ペア)を許すので、プロパティが配列であるマルチバリューのオブジェクトにするしか方法がないのです。詳しくは 「マイクロデータ(microdata)を理解するために -- プロパティリストとマルチバリューのオブジェクト」参照。
フォームからのデータが変換されてCatyの内側に入ると、そこは型システムに支配されている世界なので、入力データはPizzaOrder型として扱うべきです。translate PizzaOrder というコマンドが次のデータを作ってくれます。
@OK {
"baseType" : "マルゲリータ",
"topping" : ["ベーコン"],
"quantity": 2
}
先頭の@OKはバリデーションが成功したことを示す目印です。それに続くオブジェクトは正しいPizzaOrder型データです。
HTMLフォーム以外からの入力
Webブラウザではなくてピザ注文の専用クライアントがあったとしましょう。この専用クライアントは、Webブラウザと同じエンドポイントにHTTPで注文リクエストを投げます。しかし、データ形式はJSONだとします。
すると、{"baseType" : "マルゲリータ", "topping" : ["ベーコン"], "quantity": 2} がいきなりWebサービスの入り口に来ます。CatyのWebアダプター(入り口のプロセッサ)は、Content-Type: application/json を見て何もしないでスクリプト・インタプリタにJSONデータを渡します。
ここまではいいのですが、マルチバリュー・オブジェクトからの変換を担当するtranslateは混乱します。必ずマルチバリュー・オブジェクトが入力だと信じていたのに、裸の文字列や整数があるのでどうしていいかわかりません。(現実のtranslateはなんとか融通したりしますが。)
今までの方針は次のようなものでした;Catyスクリプト・インタプリタの外の世界における表現形式は隠蔽してしまう。しかしこの方針は、外の世界のデータ表現形式が多様化すると、どうも具合が悪いのです。考えられる対策は2つあるでしょう。
- Webの入り口を担当するプロセッサにもっと頑張ってもらう。
- translate、またはtranslateを補助するコマンドに頑張ってもらう。
Catyコアの外部に存在するWebの入り口プロセッサに型システムの情報を渡したりするのは気がすすまないので、入り口での自動変換をやめてしまって、「translateもっとがんばれ」で行こうかな、と思っています、今のところ。