ハクソク

世界を動かす技術を、日本語で。

XMLは手頃なDSLです

概要

  • IRSが新しい**Tax Withholding Estimator (TWE)**をリリース
  • TWEはオープンソースで、初めて一般からの貢献を受け入れ
  • XMLによる宣言的な仕様で税計算ロジックを記述
  • Fact Graphエンジンを利用し、計算の透明性と追跡性を実現
  • JSONよりもXMLが適している理由を解説

IRS新Tax Withholding Estimator (TWE)の概要

  • 2026年3月13日、IRSが新しい**Tax Withholding Estimator (TWE)**を発表
  • TWEは無料かつオープンソース、初めて一般からのコントリビューションも歓迎
  • 利用者は収入や控除額などを入力し、年末の納税額を推定・給与天引き額を調整可能
  • TWE開発を通じて得られたパブリックセクターソフトウェア開発の知見の共有
  • 本記事は個人の立場で執筆、公式見解ではない点を明記

XMLの再評価とFact Graphの仕組み

  • XMLは古臭い・冗長との評価が多いが、宣言的な仕様言語として現代でも有用
  • Fact Graphは、税法を**Fact Dictionary(事実辞書)**としてXMLで記述し、依存関係をもとに計算
  • 例:/totalOwedは/totalTaxから/totalPaymentsを引いた値として定義
    • XML例:
      <Fact path="/totalOwed">
        <Derived>
          <Subtract>
            <Minuend>
              <Dependency path="/totalTax"/>
            </Minuend>
            <Subtrahends>
              <Dependency path="/totalPayments"/>
            </Subtrahends>
          </Subtract>
        </Derived>
      </Fact>
      
  • 冗長だが構造が明快で、数式の意味を直感的に把握可能

具体的なXML定義例とその意義

  • リファンダブルクレジット(還付可能税額控除)は複数の控除を合算
  • ノンリファンダブルクレジットは税額をゼロまで減らせるが、マイナスにはならない
    • <GreaterOf>演算子で「ゼロまたは差額の大きい方」を選択
  • 入力値<Writable>で定義、金額なら<Dollar/>, 真偽値なら<Boolean/>を指定
  • Fact同士の依存関係により、最終的な税額を導出

宣言的仕様のメリット

  • JavaScriptなどの命令型言語では計算過程や中間値が失われやすい
    • 例:const totalOwed = totalTax - totalPayments
  • 宣言的なFact Graphでは「何をどう計算するか」のみ記述し、計算順序や実装詳細を意識しない
  • 監査性・追跡性が高く、「なぜその値になったか」を容易に説明・検証可能
    • 例:TurboTaxの“Tax Knowledge Graph”も同様の思想だが、IRSのFact Graphはオープンソースかつパブリックドメイン
  • 複雑な税制ロジックの透明性・拡張性を確保

XMLとJSONの比較

  • 計算ロジックの宣言的表現にはXMLが適している
  • JSONはデータ転送や設定ファイルには良いが、複雑なDSL(ドメイン固有言語)には不向き
  • XMLは階層構造・拡張性に優れ、Fact Graphのような複雑な依存関係を扱う用途に最適
  • JSONによる同等の記述は冗長で可読性が低下しやすい

まとめ:パブリックセクター開発におけるXMLの意義

  • 税制のような複雑なビジネスロジックは宣言的DSLで明示的に表現すべき
  • XMLベースのFact Graphは、拡張性・透明性・監査性が高い
  • オープンソース化により、誰でも仕様を確認・拡張・修正可能
  • 今後の公共ITシステム開発において、XMLと宣言的設計思想の活用が期待

Hackerたちの意見

いい記事だけど、実はこのリンク先の投稿[0]の方がもっと良かった。著者がXMLがブラウザ戦争に負けたせいで、現代のウェブ開発ツールがたくさん生まれたって説明してるんだ。追記:もちろん、JSONのツールが増えたのはJSONが共通語になったからだよ。XMLが解決してたJSONの欠点に対処する必要が出てきたってことね。0: https://marcosmagueta.com/blog/the-lost-art-of-xml/
よくあるケースだよね。「複雑なことを学ぶのは面倒だから、簡単なものをくれ」って。2年後、「あれ、もっと機能が必要だ、問題が思ったより複雑だ」。
両方読んだけど、どちらもXMLの悪い時代にAPIと一緒に働くことがどうだったかを見落としてる気がする。確かにXMLは説明的だけど、プログラマーにとっては扱いにくいんだ。XMLベースのプロトコルを使うクライアントやサーバーは、XML文字列をその言語で意味のあるメモリ内データ構造(辞書やオブジェクト、配列など)にマッピングするための独自のエンコーダ/デコーダを持たなきゃいけなかった。これらはしばしば大きくて、メンテナンスも大変だった。JavaやC#のような言語には、XMLをオブジェクトにマッピングするための魔法のライブラリがあったけど、XMLの一部しかサポートしてなくて、XMLがその靴に合わないと、95%まで行っても最後の5%を入れる方法がなくて、ひどいストリーミングXMLパーサー(SAXみたいな)で全てを書き直さなきゃいけなかった。JSONは完璧ではないけど、ほとんどの言語が持ってるデータ構造(配列、オブジェクト、辞書)にきれいにマッピングできるから人気が出たんだ。それが理由で、他に理由はない。絶対に「流行」とかそんな馬鹿げた理由ではないよ。何十万人もの開発者がXMLストリームを生成して解析するのに20%の労力を費やすことに疲れ果ててたんだ。ほんとにひどかった。そして、XMLスキーマを設計しようとする人たちの終わりのない会議についてはもう話したくもない。これを属性にすべきか、子要素にすべきか?リストの中で異なる子要素を混ぜるのを許すか、それともパーサーをシンプルにするために間接レベルを追加するか?みんなが最もエレガントだと思うことについて違う考えを持ってて、結局どれも重要じゃなかった。JSONはAPIデザインにおいて、Prettierがタブとスペースの議論に与えた影響と同じことをしたんだ。
著者が「(XML)はJavaScriptが勝ったから捨てられた。ブラウザが勝った。」って言ってるのはよく分からないな。ブラウザはXMLもJavaScriptもサポートしてたし。「AJAX」の「X」はXMLのことだし、「XMLHttpRequest」は元々XMLでデータを取得するために使われる予定だったんだ。後にJSONデータを取得するために使われるようになったけど。JavaScriptがXMLを捨てる理由にはならないよ。開発者コミュニティがXMLを使ってみたけど、全然好きじゃなかっただけなんだ。開発者コミュニティが「正しかった」かどうかはコメントしづらいけど、リンク先の記事は愚痴が多くて、文脈の詳細が少ないからね。例えば、JSONのようなシンプルなフォーマットが「協力するサービス間の小さなデータ転送やスキーマ検証が過剰になるシナリオ」には適しているって認めてるし。じゃあ、彼らは「ドキュメント」や「ファイル」をJSON形式で保存する人たちのことを言ってるのかな?それもあると思うけど、他のフォーマット、例えばYAMLと比べてJSONがそんなに一般的に使われてるとは思えないな(ブラウザでJavaScriptが勝ったからってのは全然関係ないし)。個人的には、XMLが捨てられたのは設計が悪かったからだと思うし、もしかしたら過剰設計もあったかも。スキーマチェックのあるシンプルなフォーマットの方が理想的だと思う。
XMLは多くの言語で正しく解析するのがすごく高コストなんだ。実際、世界中のほとんどは3つのオープンソース実装(libxml2、expat、Xerces)に依存してる。これらを使わないと、実際の準拠には近づけないからね。それでも、いくつかの課題にぶつかることがある(libxml2は最近ほとんどメンテされてなかったけど、多くの他の言語のバインディングの基盤になってる)。SGML由来の言語の主な特徴は、「リスト」を第一級オブジェクトにして、ネストを第二級にしていること(「終了」タグを必要とするからね)。メタデータを追加するための2つの軸があって、一つはタグ名、もう一つは属性。だから、いろんなことに適したDSLではあるけど(ウェブコンポーネントの定義でも新たに使われてるし)、実際にはXMLに似た言語の話をしてるだけで、XMLそのものではないんだ。XMLそのものを使うなら、「安価」という概念は捨てなきゃいけない。ここで言いたいのは、命令的に見えるDSLでも、宣言的に解釈できることがあるってこと。例えば、totalOwed = totalTax - totalPayments、totalTax = tentativeTaxNetNonRefundableCredits + totalOtherTaxes、totalPayments = totalEstimatedTaxesPaid + totalTaxesPaidOnSocialSecurityIncome + totalRefundableCreditsって書いても、XMLに似たDSLと同じ意味になるんだ。命令的に見える宣言的な言語の一例はMETAFONTだよ。例えば、https://en.wikipedia.org/wiki/Metafont#Example(例がうまく示せてないかもしれないけど、すべての方程式を並べ替えても同じ結果が得られるはず)。
ちなみに、これもMathMLが数学の「入力」言語にならなかった理由の一つなんだ。レイアウト重視の(La)TeXが事実上の標準として残ってる。入力の使いやすさは重要で、正確性を高めるからね。通常、厳密でセマンティックなまま(例えば、LaTeXはPlain TeXよりもレイアウト重視ではない)保つことができる。
これでドメインの表記はできるけど、処理するエンジンが必要だよね。Prolong+CLPFDはそれに合ってるかも(税のドメインにはあまり詳しくないけど)。グリーンスプンの第10ルールをこの組み合わせに言い換えることもできるかも。
> XMLは安い[...] > XMLは多くの言語で正しく解析するのが非常に高コストだって有名だよね。これがトップコメントで嬉しい。企業向けのJavaとXMLに関してはかなりの経験があるけど、XMLは安いなんてことは全然ないよ。実際、XMLで何かトリビアルじゃないことをするのは、メモリとCPUのボトルネックになることが多かった。
ここに著者がいるよ。これには同意するし、宣言的な仕様を命令的な数学表記のようにすることを妨げるものは何もないってことは重要だと思うけど、ちょっと本質から外れてる部分もあるよね。確かに、自分だけのカスタム言語を作ることはできるけど、そうするとこの記事が言ってる問題が生じるんだ。つまり、使いたい場所ごとにパーサーを移植しなきゃいけなくなるってこと。今はすべての構文の決定をしなきゃいけないしね。中置記法を使いたいなら、演算子の優先順位についてたくさんの選択をしなきゃいけない。この記事ではドメインを説明するためにシンプルな関数をたくさん使ってるけど、switch文もあるよね。あれはどう表現するの?共通の数学表記がない関数、例えば段階的な掛け算とかも同じだよ。これらはすべて解決できるけど、パーサーがもっと複雑になって、実装が一つしかない状況になりがちなんだ。もし、接頭辞記法や括弧に標準化しようとすると、s式が必要になる(この記事でも触れられている選択肢だね)。この文脈で「安い」というのは、すぐに解析できるライブラリがどの環境にもあって、ドキュメントをクエリするための成熟したツールがあるってことなんだ。XML DSLに新しいアイデアを追加しても、解析の複雑さは全然増えないよ。これって小さなチームにはすごく助かる!タイトルの「安い」って言葉には悩んだけど、「コスト効果が高い」とかもっとポジティブな言葉を使おうかとも考えた。でもやっぱり「安い」が正しいと思う。構文でコスト削減の選択をしてるわけで、それにはOPが指摘してるように表現力のトレードオフがあるけど、多くのドメイン、特に指定しているものを広く(安く)構築できるようにしたい場合には、絶対に正しい選択なんだ。
XMLが犯した同じ間違いを繰り返してる人たちをよく見るけど、学んでないんだよね。問題をこう説明するよ:> インターチェンジフォーマットに機能を追加すればするほど、そのフォーマットは解析が難しくなる。JSONが人気なのには理由があって、サポートする機能が少ないからこそ、インポートが本当に簡単なんだ。一方でXMLは属性、名前空間、CDATA、DTD、QName、xml:base、xml:lang、XIncludeなどなど、いろいろサポートしてる。キッチンシンクまで全部詰め込まれたんだ。最近、複雑さを減らすためにSqliteをインターチェンジフォーマットとして使うってスレッドがあったけど、Sqliteはアプリケーション特化型のデータストアとしては好きだけど、XMLと同じように機能がたくさんあって、データストアには良いけど、複数のプロデューサーやコンシューマーがそれぞれのアイデアを持つインターチェンジフォーマットには最悪なんだ。CSVは仕様が少ないかもしれないけど、生成や消費が簡単だから人気があるんだ。残念ながら、最近JSONを壊す人たちがいて、例えばフォーマットにコマンドを追加したり、その「コメント」をデータ(例えば型情報)を保持するために使ったりして、解析が必要になる。これはXML属性の悪いバージョンだよ。
XMLの複雑さの多くは、既存の文字やデータエンコーディングとのラウンドトリップ互換性を持たせたいという欲求や、SGMLとの前方互換性を持たせたいという欲求から来ている。特定の「プロファイル」のXML(例えば、UTF-8のみ、ユーザー定義のエンティティや一般的なDTDサポートなし)だけをサポートするパーサーは、言語の99%の価値を捉えつつ、もっとシンプルで効率的になれるんだ。
本当に大規模なDSL仕様をコンパイルしているわけじゃないなら、解析の速度を最適化する必要はないよ。この用途にXMLを使うなら、DOMを使っても十分速いしね。心配なのは、無限に解析が続く問題だけど、これを制御する方法はいくつかあるよ。
それ、変なコメントだね…。ここでの「安い」は、記事での「安い」とは意味が違うんだよ。ここでは「CPUにどれだけ負荷がかかるか」って意味で、記事では「DSLを指定して広くサポートするのがどれだけ難しいか」ってこと。君が投稿したコードも、著者自身が「悪くない」と認めているもので、JavaScriptに変換する際に実装の詳細が漏れる一つの病的な例は省いているよね。なんか、著者が言いたいことを理解しようとせずに、読む前から著者が間違ってるって決めつけてたみたいに見えるよ。
そのコードを宣言的にするためにDSLを指定する必要すらないよ。数値の代わりに式オブジェクトを操作する実際のコードでもいいし(ただしJavaScriptではオペレーターのオーバーロードがないけど)、式オブジェクトのグラフが結果になるんだ。
君の最初の反論はちょっと細かすぎる気がするな。 > だから、多くのことに適したDSLではあるけど(ウェブコンポーネントの定義でも新たな活躍を見せている)、基本的にはXMLに似た言語の話をしているだけで、XMLそのものではないんだ。XMLそのものに行くなら、「安い」って言葉は捨てなきゃいけない。でもTWEはそのあたりを受け入れてないよ。目的には必要ないからね。それを理由に「XMLに似ている」と呼ぶのは変だと思う。客観的に見てXMLだよ。全てのXML機能を使ってるわけじゃないけど、それでもXMLなんだ。まるで「スクールバスはバスじゃない、ただのバスに似ているだけだ」って言ってるみたい。バスにはカップホルダーがあったりするけど、スクールバスにはない。だからスクールバスは本当のバスじゃないってこと?その妥当性や関連性が見えないな。
それとも、見た目が良くて埋め込み型ドメイン特化言語(eDSL)に優れたサポートがあるプログラミング言語(Haskell、OCaml、Scalaなど)を使うって手もあるよ。あるいは、持ってる言語(JavaScript)をちゃんと使うとかね。例えば、`sum`の抽象を追加する代わりに`.reduce((acc, val) => { return acc+val }, 0)`を使うとか。特に「すべての計算が単一のユーザー入力でブロックされる」問題は、例えばアプリケーティブやアローを使うことで解決できる(これらはかなり単純な抽象代数の概念だけど、ほとんどのプログラマーには馴染みがない)。もちろん、あまりにも抽象的な関数型プログラミングの概念で複雑にする誘惑には注意してね。XML DSLを書くと、1.「どの部分を並列化して独立に評価できるか」という問題を解決しなきゃいけない。まあ、この問題は関数型プログラミングや抽象代数、カテゴリー理論の概念でずっと前に解決されてるけど。2. 見た目が悪い(個人的に)。3. 他のプログラマーには読めない全く新しい語彙を作ってる。4. ドメインが非自明なら、グリーンスプンの第10ルールに引っかかる可能性が非常に高い。
Rakuもそのリストに加えてほしいな。初期のRaku開発者たちはみんなHaskellのコーダーだった(最初のRakuパーサー(PUGS)はHaskellで書かれた)。Rakuはオブジェクト指向と関数型のコーディングスタイルの両方をサポートしてて、文法も組み込まれてるから、DSLにはとても良いんだ。
それかLispだね。「良さそう」って意見はみんなが同意するわけじゃないかもしれないけど、S式を見た後だとXMLはひどく見えるよ。冗長すぎて重たい。
> プログラミング言語を使えばいいんじゃない?… HaskellやOCaml、Scalaみたいな。そうすると、これらの言語に熟練した開発者を見つける問題にぶつかるよね。僕は多分一番頭が良いわけじゃないけど、30年近く有能なプログラマーをやってきたよ。Haskellは、数回挑戦したけど本当に難しかった。
HTML!
S式も安価なDSLだよ。私は今開発中のwasmで動くデスクトップブラウザランタイムで使ってる。実際、「HTML」^1とCSS^2として使ってるんだ。これがうまく機能するから、ドキュメントの漂流を防ぐためにデザインしたマークアップ言語のHTMLエクスポートのスタイリングにも再利用してるよ^3。1. https://gitlab.com/canvasui/canvasui-engine/-/blame/main/exa... 2. https://gitlab.com/canvasui/canvasui-engine/-/blob/main/exam... 3. https://gitlab.com/sablelang/libcuidoc
S式は最高だよ。パーサーを実装するのがめっちゃ簡単だからね。しばらくの間、S式のパースと評価を技術的なコーディングスクリーンの面接質問に使ってたんだけど、面接の範囲内でS式を使って機能的(ダジャレ)なプログラミング言語を実装するのが可能なんだ。面接の本題ではないけど、候補者が動作するプログラミング言語を実装したと気づいた瞬間の顔が輝くのを見るのが一番の楽しみだった。
これはDSLじゃないよ。一般的なレキサーとパーサーなんだ。テキストを受け取って、抽象構文木を生成する。実際のDSLは君の仕様と適用する構文だよ。これは多くの同等のパーサーツールの一つで、特に冗長な方だね。だから、手書きじゃないものには最適だけど、生成されたテキストにはまあまあ使える。普及してるからこその利点もあって、ツールキットが豊富なんだ。機能が多すぎて(ちょっと冗長な)複雑だけど、その中の一つが君のユースケースにぴったり合うこともあるよ。
参考までに、記事よりもJSON構造をうまく使えるよ: {"GreaterOf": [ {"Value": [0, "Dollar"]}, {"Subtract": [ {"Dependency": ["/totalTentativeTax"]}, {"Dependency": ["/totalNonRefundableCredits"]} ]} ]} 基本的に、ノードは一つのエントリーを持つオブジェクトで、そのキーがタイプで、値が配列なんだ。かなりS式っぽいアプローチだね。本当に配列を使いたくないなら、葉の部分で普通の値を使うこともできるよ: {"GreaterOf": [ {"Value": {"value": 0, "kind": "Dollar"}}, {"Subtract": { "minuend": {"Dependency": "/totalTentativeTax"}, "subtrahend": {"Dependency": "/totalNonRefundableCredits"} }} ]} こうすると、オブジェクトのキーが並び替えられても、内容を見る前にタイプが必ずわかるから、任意の量のJSONをバッファリングせずにストリーミングデコードができるっていう良い特性があるんだ。税法を解析する時にはあんまり重要じゃないかもしれないけど、大きなデータセットには役立つかもね。
同意。ファクトグラフを使いたい言語は、選んだDSLを「解釈」しなきゃならないし、JSONはXMLよりも一般的で、解析もずっと簡単だよ。それに、この記事が使ってる意味では、コストも安いしね(XMLドキュメントを頭の中でどれだけの言語で解析して歩ける?JSONはどう?)。JSONが簡単な理由を考えてみて。依存関係なしにファクトグラフを解析して解釈するために必要なコードの合計がどうなるか想像してみて。XMLだと、ハッシュマップで複雑な状態を持ち運び、オープン/クローズタグを一致させるために文字列を比較しなきゃいけない。DSLが属性や子ノード、テキストコンテンツをどう使うかによってさらに複雑になるし。JSONなら、オープン/クローズの[] {}といくつかのリテラルを一致させるだけで済むんだ。そうすれば、結果のASTの宣言部分をすぐにスキミングできる。XMLライブラリがその複雑さを隠しているから、これを無視するのは簡単だけど、確かに仕事はこなせる。でも、他の人が指摘しているように、こういう決定が積み重なって、コンピュータがどんどん速くなっているのに遅延が悪化する結果になるんだよね。
美的に言うと、そんなJSON構造は退化してると思う。まるで、ECMAScriptアプリを作るのに、すべてのクラスや構造が一つのメンバーしか持てないような感じ。タグ付きデータが欲しいなら、そういう表現を選べばいいのに。
嫌なのは、クソみたいな引用が多すぎること。JSONを見ると、ただの雑音にしか見えない。まるでスクリーンショットを撮って2D FFTをしたみたいに、JSONは他の多くのフォーマットに比べて高周波成分がたくさんある。だったら、clojureのEDNの方がいいな。
XMLは税務当局に大人気だよね。ポーランドの税務当局は、eドキュメントやオンライン申告が大好きなんだ。ただ、彼らのXMLドキュメントは完全に人間には読めないんだよ。スキーマが紙のフォームのフィールド番号に基づいてるからね。新しく作られた国のe請求書システムでも、紙のフォームは一切使ってないのに、ほとんどのフィールド名が「‹P_19N›1‹/P_19N›」みたいになってる。XMLスキーマを読んでみると、「商品やサービスの提供が、[VAT]法第43条第1項、法第113条第1項および第9項、または法第82条第3項に基づく規則や他の規定により免税であることの欠如を示すマーカー」って書いてある(もちろんポーランド語だからGoogle翻訳)。だから、私の請求書は「はい[1]、私は[N]、$allThatNonsense [P_19]の下で免税ではありません」って言ってるわけ。ちなみに、VAT法の主要著者が税務コンサルティングサービスを提供してるらしいよ、登録税理士#00001としてね。
XMLは結構いいマークアップ言語で、まあまあなデータ交換フォーマットだよ(完璧ではないけど、ツールは結構良い)。でも、プログラミング言語として使われているのを見るたびに、深く後悔してる。比較すると、JSONはひどいマークアップ言語だけど、かなり良いデータ交換フォーマットで、やっぱりプログラミング言語としては深く後悔してる。誰かが純粋なJSONでプログラミング言語を作ったかは分からないけど(たぶん作ったと思う、ぞっとする)、ansibleにはかなりのプログラミング構造があって、YAMLに入ってる。YAMLはJSONを設定言語の服を着せたものだからね。でも、私のJSON批判に対する反論として、そこから decent な言語を作ることもできるかもしれない。lispを見てみて、S式はデータ交換フォーマットの一種(大体JSONに相当)で、結構良い言語だよ。
XMLでもJSONでもプレーンテキストでも、何でもいいんだ。重要なのは、ドメイン言語を話すこと。自分のドメインの言語で話し、設定やデータをドメインとユーザーの言語でモデル化すること。それがすごく強力で、ドメイン駆動設計が今でも強力な概念である理由なんだ。
XMLやC#をいじってる人に役立つかもしれないけど、Visual Studioには「XMLをクラスとして貼り付ける」機能がメニューにあるよ。それは、デシリアライズするつもりなら結構便利だよ。