Scalaのmatch式(2)

ScalaXMLリテラルを使ったmatch式を試してみました.作ったのは次のようなちょっと恣意的なテストプログラムです.

  def main(args: Array[String]): Unit = {
    val doc = <nsb:block xmlns:nsb="urn:block">
       <nsi:inline xmlns:nsi="urn:inline1" seq="1">phrase1
    <nsi:inline xmlns:nsi="urn:inline2" seq="2">phrase2
    </nsi:inline>
    </nsi:inline>
    </nsb:block>
    val nodeSeq = doc \\ "inline"
    for (node <- nodeSeq)
      node match{
      case <inline>{child @ _*}</inline> => println("<inline>'s nsmaspace="+node.namespace+" name="+node.label)
      case other => println("Other:"+other)
    }
  }

docという変数にXMLリテラルを記述していますが、最初のinline要素はurn:inline1ネームスペースに属します.次のinline要素はurn:inline2ネームスペースに属します.ちょっといやらしいデータです.

まずval nodeSeq = doc \\ "inline"でnodeSeq要素にすべてのinline要素を選択させています.ここでval nodeSeq = doc \\ "nsi:inline" と書いても何も選択されません.これは、\\ がXPathのlocal-name()でしか要素を選択できないことを表しています.

さてこのプログラムを走らせると

<inline>'s nsmaspace=urn:inline1 name=inline
<inline>'s nsmaspace=urn:inline2 name=inline

と出力されます.ということは、XMLパターンはlocal-name()でマッチングしてくれるのか?ということになります.ところがプログラムをちょっと変えて、

 case <nsi:inline>{child @ _*}</nsi:inline> => println("<nsi:inline>'s nsmaspace="+node.namespace+" name="+node.label)

としてみたらどうでしょうか?なんとちゃんとマッチングして

<nsi:inline>'s nsmaspace=urn:inline1 name=inline
<nsi:inline>'s nsmaspace=urn:inline2 name=inline

と出力されます.これはXPathのname()でマッチングしていることになります.XMLパターンと言うのはいったいどういう仕様で実装されているのか疑問が湧いてきてしまいます.善意に解釈すれば、case <nsi:inline>{child @ _*}</nsi:inline> と書いてもXMLパターンはネームスペースプリフィックスとネームスペースをVBのように紐付けする手段をScalaは持ち合わせていないので、ネームスペースプリフィックス部分を無視してマッチングしているのかもしれません.

それではと考えて、

 case <nsix:inline>{child @ _*}</nsix:inline> => println("<nsix:inline>'s nsmaspace="+node.namespace+" name="+node.label)

XMLパターンをありえないネームスペースプリフィックスに変えると

case other => println("Other:"+other)

にマッチングして

Other:<nsi:inline xmlns:nsi="urn:inline1" xmlns:nsb="urn:block">phrase1
    <nsi:inline xmlns:nsi="urn:inline2">phrase2
    </nsi:inline>
    </nsi:inline>
Other:<nsi:inline xmlns:nsi="urn:inline2" xmlns:nsi="urn:inline1" xmlns:nsb="urn:block">phrase2
    </nsi:inline>

と出力されます.ますます訳がわからなくなりました.

あとXMLパターンで誰でも一度はやりたいと考えるのは、

case <nsi:inline seq="1">{child @ _*}</nsi:inline> => println("<nsi:inline seq="1">'s nsmaspace="+node.namespace+" name="+node.label)

XMLパターンの中に属性に関する条件を記述できないか?というものです.でもこの期待は見事に外れます.ScalaコンパイラはこのXMLパターンに

Multiple markers at this line
- in XML literal: '>' expected 
 instead of 's'
- ')' expected but integer 
 literal found.
- in XML literal: '>' expected 
 instead of 's'

というエラーを出します.つまりXMLパターンには属性の条件は書けません.

という訳でXMLパターンはイマイチであることがわかります.では他に方法はないのでしょうか?次ではコンストラクタパターンでこのような問題が解けないか考えてみます.