Catyの型システムとサイト自動生成 (2)
いよいよサイト自動生成の話。
サイトを構成するページの作成をデザイナに依頼することを想定する。静的ページはコンテキストがvoidのテンプレートと考えられるので、すべてのページをテンプレートと考えてよい。
デザイナは、作成するテンプレート(静的ページ含む)の仕様を渡されて作業をする。仕様にはコンテキスト型の記述が含まれるが、コンテキスト型とテンプレートが1:1対応しているわけではない。ハイパーリンクグラフのabstractではない状態ノードとテンプレートが1:1対応する。
- 具体状態ノード ←→ テンプレート (1:1対応)
具体状態ノードは、型とリンク記述からなる。
- 具体状態ノード = (型 + リンク記述)
リンク記述まで含めた型概念をハイパー型と呼び、それに対するハイパースキーマとハイパーバリデーションの概念があるのだが、完全な定式化ができてないので、具体的状態ノードの型(ハイパーリンクは省略)を中心に話をする。(たぶん、ハイパー型とはハイパーリンクグラフの仕様となるだろう。)
状態ノードと型とテンプレート
S, Tなどは型として、状態ノードを α::S、β::T などと書く。α、βなどはノードの名前(ラベル)である。α≠β(名前が違う)とき、α::T と β::T は別なノードとなる。状態ノードの型は、その状態(画面、ページ)を生成するテンプレートのコンテキスト型と考えてよい(正確に言えばちょっと違うが)。
同じ型Tを持つ2つの状態 α::T と β::T に異なるテンプレートが必要となるのは、αとβのサイト内での役割/用途/文脈などが違うためである(それゆえに違うラベルを与えている)。その違いを示すために異なるデザインにするかも知れない。しかし、これは知的な話なので、非知的な自動生成では、型とテンプレートを対応付けてしまう。α::T と β::T のテンプレートは、内容は同じで名前だけが違うモノとなる。
以上の単純化により、テンプレート作成作業とは:
- 与えられた型(状態の型=コンテキスト型)1つに対して、1つのテンプレート(静的ページを含む)を作る。
- 一般にN個の型(N≧1)が与えられるので、テンプレートもN個となる。
- サブテンプレートやテンプレート関数も含めると、N個とは限らないが、これは単に数え方の違いに過ぎない。アクションから直接呼ばれるトップレベルテンプレートはN個である。
型からのテンプレート生成
Ψ、Φなどのギリシャ大文字は型の有限集合を表すことにする。ギリシャ大文字を使った理由は、型やインスタンスと区別するためである。型の有限集合が「スキーマ=仕様」に対応する。T∈Ψ に対して、Tに対応するテンプレートを a(T) とする。aがデザイナによる作業を表す。作業aを人間ではなくて、プログラムにautoでやらせるのが目標。
コンテキスト型がTであるテンプレートテキストの全体を TemplTextT、または TemplText<T> と書くことにする、T=void ならば静的ページを意味する。。多相型 ∀X.TemplText<X> = forall<X>TemplText<X> を、ワイルドカードを使って TemplText*、または TemplText<*> とも書く。
テンプレートを生成する作業は、a:Type→TemplText<*> という写像となる。次の条件を満たす必要がある。
- a(T)∈TemplText<T>
これは、「型Tに対するテンプレートのコンテキスト型はTであるべき」という当たり前の条件。この条件をコンテキスト条件と呼ぶことにする。
テンプレート展開のevalであるexpandを使うと、f(t) := expand(t, a(T)) として、写像 T→Text を定義できるので、a(T) は指数型 [T, Text]に入ると思ってもよい。つまり、aは、(Type → [*, Text]) という写像とも解釈できる。ただし、aがType全域で定義されいるとは限らないので、部分写像と考える。
一般に、コンテキスト条件を満たす Type→TemplText* という写像は「型からテンプレートを生成する操作」なので、サイト自動生成に使える。ただし、与えられた仕様(型の有限集合)とマッチしなくてはならない。
- 部分写像 a:Type→TemplText* がΨ(Ψ⊆Type)と適合するとは、Ψ⊆(aの定義域) のこと。
レイフィケーション
Typeは普遍データ領域Uの外側にある。Type→U という埋め込みを型レイフィケーションと呼ぶ。型レイフィケーションも全域でなくて(部分レイフィケーションで)もよいし、いくつかのレイフィケーションがあってもよい。なにか標準的なレイフィケーションを1つ選んで、それをReiとする。Rei:Type→U (埋め込み写像)。
a:Type→TemplText* に対して、次の性質を満たすa'を考える。
- T∈Type に対して、a'(Rei(T)) = a(T) が(両辺が定義されるなら)成立する
a'は、Uの外に存在するaをU内に落としたもの。a' は U→TemplText* だが、TemplText*⊆Text なので、a':U→Text という部分写像と考えてよい。a'は次の性質を持つ。
- Uの適当な部分集合上で定義されている。
- 値はテキストである。
つまり、なんてことないコマンドである。a'をメタレベルで解釈しなおすと、a:Type→TemplText* という高階写像となる。
高階テンプレートとレイフィケーション
型からテンプレートを生成する機能であるaを定義する際に、再びテンプレート技術を使うことができる。このとき使うテンプレートは、次の意味で高階である。
- コンテキスト(入力)がTypeの要素なので、高階の対象を入力とする。
- 結果(出力)が、指数型 [T, Text]とみなせるので、出力も高階の対象である。
「高階の対象」という言葉を使ったが、入力は「集合(型)の集合」、出力は関数型(指数型)である。レイフィケーションを使うことにより、aをa'に落とせる。a'を定義するテンプレートでは:
- コンテキスト(入力)はUの要素なので、単なるデータである。(意味は高階の対象だが。)
- 結果(出力)はTextなので、単なるテキストデータである。(テンプレートテキストである必要があるが。)
「集合の集合」に「関数」を対応させる、という高階の写像aも、レイフィケーションを通じて、単なるテンプレートa'で実現できる。実際のサイト自動生成では、静的ページやフォームの自動生成が難しい。情報表示用のテンプレート(サーバーサイドJSONView)は実は簡単。特に、(型→フォーム) という生成写像はauto-formと呼ぶ。高階テンプレートの一番の用途はauto-formの実現である。
クォーティングによる高階テンプレートの実現
テンプレートテキストが単なるテキスト以上のものとなる根拠は、区切り記号とそれによるブロック構造である(http://d.hatena.ne.jp/m-hiyama/20070125/1169702291)。異なる区切り記号を持つ2つのテンプレート言語は、互いに他のメタテンプレート言語として使用できる。
もっと簡単な方法は、区切り記号のクォーティングまたはエスケープである。クォートされた区切り記号は、その評価が1回遅れる(何回でも遅らせることができるが)。この機能(クォーティングだけ)で高階テンプレート機能を実現できる。
例を示す。
次が実行時のテンプレート変数(コンテキスト)。
- title
- date
- content
生成時のテンプレート変数(メタコンテキスト)。
- siteTitle
- encoding
<?caty-meta template="smarty-mx" ?><<?caty-meta template="smarty-mx" ?>> <html> <head> <meta http-equiv='Content-Type' content='text/html; charset={$encoding}' /> <title>{$siteTitle} - {{$title}}</title> </head> <body> <h1>{$siteTitle}</h1> <h2>{{$title}} : {{$date}}</h2> {{$content|noescape}} </body> </html>
展開を1回した後。
<?caty-meta template="smarty-mx" ?> <html> <head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <title>キマイラ飼育記 - {$title}</title> </head> <body> <h1>キマイラ飼育記</h1> <h2>{$title} : {$date}</h2> {$content|noescape} </body> </html>
さらに展開。
<html> <head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <title>キマイラ飼育記 - 最近思うこと</title> </head> <body> <h1>キマイラ飼育記</h1> <h2>最近思うこと : 2012-04-01</h2> <p>最近思うのだが ...</p> </body> </html>