というわけで、テンプレート処理は一律に次の関数で表現できます。
expand(parent_context +<
eval(input, read_expr(templ_path), params),
read(templ_path))トップレベル・テンプレート処理のときは、parent_contextが空({})になるだけです。
なんかこれは複雑な気がしてきた。
expand(eval(parent_context, read_expr(templ_path), params),
read(templ_path))
これで十分かな。
サブテンプレート(子供のテンプレート)の環境には、親のコンテキストが入力データとして入ってきます。実際には、この入力データが親コンテキストなのか外部から来たのかは、子供にはわかりません。単に「入力があります」というだけ。トップレベルのテンプレートにおけるparent_contextは、トップレベル環境における入力ってことです。
ですから、parent_contextって呼び方がふさわしくなくて:
expand(eval(input, read_expr(templ_path), params),
read(templ_path))
親も子も変わらなくて、inputの由来も気にしなくていいということです。追加するルールは次の1つだけです。
- サブテンプレートを評価するときは、親のコンテキストを子の評価環境の入力として使う。子が入力を加工しなければ、「親のコンテキスト=子のコンテキスト」となる。
「親のコンテキスト=子のコンテキスト」は、テレンス・パーが「なんだかんだ言っても*1、これが最良だ」と推奨していた方法ですね。
[追記]
関係者向け補足:
---[toplevel.html]
|
+---[child-1.html]
|
+---[child-2.html]
|
+---[grandchild-2-1.html]
|
+---[grandchild-2-2.html]
というようなインクルード階層構造があると、入力データの流れは:
/* 向きは左から右へ */-[toplevel.icaty]->(コピー分配)
+-[child-1.icaty]->
|
+-[child-2.icaty]->(コピー分配)
|
+-[grandchild-2-1.icaty]->
|
+-[grandchild-2-2.icaty]->
展開結果の埋め込み手順は上のインクルード階層構造そのものだが、向きは子から親へとなる。
<--[toplevel.html]
^
+<--[child-1.html]
|
+<--[child-2.html]
^
+<--[grandchild-2-1.html]
|
+<--[grandchild-2-2.html]
考えられる限り一番単純。
入力(input)とパラメータ(params)のなかで、パラメータはパス名に付加してしまっていいと思います。つまり、クエリ文字列方式です。
これによって、evalが利用するパラメータ {"name": "hiyama"} が得られます。Webからのリクエストのときとまったく同じ方式なので統一性もあるかと。
{include file="header.tpl?name=hiyama"}
これは使うとすると、親も子も、情報(入力+パラメータ)の渡し方は完全に同じ。
*1:関数呼び出しの場合は、このようなスコーピングは間違いのもとで、僕は大嫌いです。