XSLT3.0への道(13) 高階関数(higher-order function)

oXygenを毎日使っていたのですが恥ずかしいことにSaxsonが9.3⇒9.4になっているのに気がつきませんでした.いろいろとこれでXSLT3.0について試すことができます.今回はXSLT3.0での関数について少し紹介してみます.
まず関数の位置づけです.きわめてそっけなく図に書いてあるだけなのですけれども関数はついに最上位レベルに位置する「型」になりました.
この二つの図を見比べるとわかります.
 
XQuery 1.0 and XPath 2.0 Functions and Operators (Second Edition)
1.6 Type Hierarchy
http://www.w3.org/TR/xpath-functions/#datatypes

XPath and XQuery Functions and Operators 3.0
1.5 Type system
http://www.w3.org/TR/xpath-functions-30/#datatypes
 
前者にはもちろんないのですが、後者を見るとfunction(*)がnodeと並んで型の階層のトップに位置するようになりました.
 
これは具体的にはどのようなことを表しているのでしょうか?
 
型として関数が認められたということは、たとえば関数型の変数 xsl:variableというものがあるということになります.また関数を引数 xsl:paramにするテンプレートもありです.また型なのですから関数型を返す関数やテンプレートもありということになります.
ホントでしょうか?ちょっと試してみました.
 
<!--関数型の変数-->   
<xsl:variable name="secondPowerOf" as="function(xs:integer) as xs:integer" select="function($a as xs:integer) as xs:integer {$a * $a}"/>
<xsl:template match="/">
  <xsl:message select="concat('$secondPowerOf(2)=',string($secondPowerOf(2)))"/>
</xsl:template>
 
とすると
 
$secondPowerOf(2)=4
 
と出力されます.動きました!変数$secondPowerOfは関数そのものなのですから、あとに()をつけて引数を指定してやれば関数が呼び出されるのです.
 
次に関数をパラメータでテンプレートに渡してみます.
 
<!--普通の関数-->
<xsl:function name="ahf:secondPowerOf" as="xs:integer">
  <xsl:param name="prmVal" as="xs:integer"/>
  <xsl:sequence select="$prmVal * $prmVal"/>
</xsl:function>
 
<!--関数を引数にとるテンプレート-->
<xsl:template name="funcParamTemplate">
  <xsl:param name="prmFunc" as="function(xs:integer) as xs:integer"/>
  <xsl:message select="concat('$prmFuncf(4)=',string($prmFunc(4)))"/>
</xsl:template>
 
<xsl:template match="/">
  <xsl:call-template name="funcParamTemplate">
    <xsl:with-param name="prmFunc" select="function-lookup(xs:QName('ahf:secondPowerOf'),1)"/>
  </xsl:call-template>
</xsl:template>
 
結果は次のように出ます.
 
$prmFuncf(4)=16
 
function-lookupは引数にある名前と引数の数を持つ関数を返してくれる関数です.
 
少し手抜きですが、関数を戻り値として返す関数はできたも同然です.
 
<!--関数を返す関数-->
<xsl:function name="ahf:getFunc" as="function(*)">
  <xsl:param name="prmFuncName" as="xs:QName"/>
  <xsl:param name="prmArity" as="xs:integer"/>
  <xsl:sequence select="function-lookup($prmFuncName,$prmArity)"/>
</xsl:function>
 
<xsl:template match="/">
  <xsl:message select="concat('$ahf:getFunc(5)=',string(ahf:getFunc(xs:QName('ahf:secondPowerOf'),1)(5)))"/>
</xsl:template>
 
結果は
$ahf:getFunc(5)=25
 
と出てくれます.
 
このように、関数を引数にしたり関数を戻り値とできるような関数を高階関数(higher-order function)と呼びます.こうしてXSLTはついに関数型言語になりました.
 
実はつい最近日経BPのニュースの配信で「関数型プログラミングは本当に難しいのか 」
という記事(読むには会員登録が必要です)を読んでいたら、次のように書いてありました.
 
『そもそも関数型プログラミングでいう「関数」とは、C言語Javaなどでいう関数とは意味が異なる。関数型プログラミングでいう「関数」とは、「入力が同じであれば、出力も常に同じ値になる」ものを指す。』
C言語などでいう関数は、同じ入力を与えたとしても関数内部の状態やシステム全体の状態によって得られる出力は変わってしまう。例えば、あるJavaの関数(メソッド)が内部で変数を保持し、出力に関与していれば、たとえ同じ入力を与えたとしても、関数の出力値はその変数の値に依存して変わってしまう。このような関数は、関数型プログラミングでいう「関数」には相当しない。』
 
なんだこれはXSLTのことじゃないかとすぐに気がつきました.XSLTでは関数は入力パラメータのみに出力が依存し、返す結果は同じです.関数からグローバル変数を参照する場合もあるでしょうが、XSLTではグローバル変数は一回初期化されてしまうと以降は値を変えないので関数の戻り値を変更するようなことはできません.
関数が型として扱えるようになり、XSLTはついにバージョン3.0で今流行の関数型言語になりつつあります.
さてこのような関数、いったいどのように使うのでしょうか?まだXSLT3.0が勧告にもなっていないので使用例がそうあるわけではありません.でも私は関数をパラメータで渡せるということに非常に魅力を感じます.
 
例えば、入力のXML文書(またはデータ)の値により処理を分岐させるのに、今まではいちいちxsl:chooseを書かねばなりませんでした.例えば関数を値に対応させて定義しておいて(mapというものを使います)、データの値により処理関数を選択し、それをテンプレートに渡して処理させるというようなことが考えられます.
渡されたテンプレートの側は、関数の実装を何も知らないで呼び出すだけです.考えてみただけでもおもしろそうでゾクゾクします.
 
でもまだそのような場面に出会うまでは時間がかかりそうです.XSLT3.0はまだワーキングドラフトで勧告になるのはまだまだ先になるでしょうから.