- Die US-Steuerbehörde hat den neuen Tax Withholding Estimator (TWE) als Open Source veröffentlicht; ein zentrales Designprinzip ist eine Struktur, die das US-Steuerrecht als XML-basierte deklarative Spezifikation modelliert
- Die Steuerberechnungslogik von TWE basiert auf einer Logik-Engine namens Fact Graph, die einzelne Steuerposten als Abhängigkeitsgraph von in XML definierten „Fakten“ darstellt
- Wenn man Steuerlogik in einer imperativen Sprache wie JavaScript implementiert, entstehen Probleme wie die Verwaltung der Ausführungsreihenfolge, der Verlust von Zwischenwerten und die Offenlegung von Implementierungsdetails; daher ist ein deklarativer Ansatz unverzichtbar
- JSON ist für die Verarbeitung beliebig verschachtelter Ausdrücke ungeeignet, während bei XML die Tags selbst den Objekttyp darstellen und es daher für den Aufbau eines DSL deutlich vorteilhafter ist
- XML erlaubt die kostenlose Nutzung eines ausgereiften Tool-Ökosystems wie XPath und ist damit die kosteneffizienteste Wahl für plattformübergreifende deklarative Spezifikationen
Fact Graph: US-Steuerrecht in XML ausgedrückt
- Der von der US-Steuerbehörde IRS veröffentlichte Tax Withholding Estimator (TWE) ist ein Tool, mit dem Steuerzahler durch Eingabe von Einkommen und Abzügen ihre Steuern und Quellensteuer schätzen können
- Das Projekt ist als Open Source veröffentlicht, Beiträge aus der Allgemeinheit sind ebenfalls erlaubt
- TWE ist eine statische Site, die aus zwei XML-Konfigurationen erzeugt wird; die erste ist das Fact Dictionary, das das US-Steuerrecht abbildet
- Fact Graph ist eine Logik-Engine, die ursprünglich für das IRS-Direct-File-Projekt entwickelt wurde und die Steuerschuld sowie die Quellensteuer eines Steuerzahlers auf Basis der im Fact Dictionary definierten Fakten berechnet
- Jeder Fakt wird in XML definiert; zum Beispiel wird
/totalOwed als abgeleiteter Fakt (Derived) dargestellt, der /totalPayments von /totalTax abzieht
- Der „Gesamtbetrag fällig“ (
total owed) ist die Differenz zwischen der Gesamtsteuer auf das Einkommen (total tax) und den bereits geleisteten Zahlungen (total payments)
- Erstattungsfähige Steuergutschriften (refundable credits) sind Steuervergünstigungen, die den Steuersaldo negativ machen können; dazu gehören Earned Income Credit, Child Tax Credit und American Opportunity Credit, die mit
<Add> aufsummiert werden
- Nicht erstattungsfähige Steuergutschriften (non-refundable credits) können die Steuerlast nur bis auf 0 senken; mit dem Operator
<GreaterOf> wird der größere Wert aus 0 und (vorläufige Steuer - nicht erstattungsfähige Gutschriften) gewählt
- Für Benutzereingaben wird statt des Tags
<Derived> das Tag <Writable> verwendet; mit <Dollar/>, <Boolean/> usw. wird der Werttyp festgelegt
- Die Fakten hängen voneinander ab und bilden eine Graphstruktur, aus der am Ende die endgültigen Steuerwerte hervorgehen
Warum Steuerlogik eine deklarative Spezifikation braucht
- Schreibt man dieselbe Berechnung in JavaScript, wirkt
const totalOwed = totalTax - totalPayments zwar kompakt, aber das ist eine imperative Vorgehensweise, bei der nach der sequentiellen Ausführung Zwischenstufen verloren gehen
- Wenn Abhängigkeiten tiefer werden, entsteht das Problem der Ausführungsreihenfolge: Eine Benutzereingabefunktion wie
getInput() blockiert alle nachfolgenden Berechnungen, und je nachdem, ob etwa ein Ehepartner vorhanden ist, müssen sich sogar die Fragen selbst ändern
- In der Logik zur Summierung von Social-Security-Einkommen werden JavaScript-Implementierungsdetails wie
map/reduce offengelegt, während <CollectionSum> in XML das steuerliche mathematische Konzept selbst ausdrückt
- Mit
<Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/> werden Elemente innerhalb einer Sammlung aufsummiert
- Das Fact Dictionary arbeitet deklarativ: Man beschreibt weder konkrete Ausführungsschritte noch deren Reihenfolge, sondern nur benannte Berechnungen und Abhängigkeiten; die Engine entscheidet dann automatisch, wie sie ausgeführt werden
- Der wichtigste Vorteil eines deklarativen Steuermodells ist die Prüfbarkeit (auditability) und interne Inspektion (introspection): Man kann das Programm fragen, „Wie wurde diese Zahl hergeleitet?“
- In imperativen Programmen sind Zwischenwerte bereits verworfen und nur noch über Logs oder einen Debugger sichtbar; bei US-Steuerrecht mit Hunderten Zwischenberechnungen ist das nicht skalierbar
- Laut Chris Given, dem ursprünglichen Autor von Fact Graph, ist Fact Graph „ein Mittel, um zu beweisen, dass nicht abgefragte Angaben das Steuerergebnis nicht verändert hätten und dass alle zustehenden Steuervergünstigungen berücksichtigt wurden“
- Auch Intuit, der Hersteller von TurboTax, kam zum gleichen Schluss und veröffentlichte 2020 ein Whitepaper zum „Tax Knowledge Graph“, die Implementierung blieb jedoch nicht öffentlich
- Der IRS Fact Graph ist dagegen Open Source und Public Domain, sodass ihn jeder untersuchen, teilen und erweitern kann
Warum XML für DSLs weit besser geeignet ist als JSON
- Wenn man JSON als deklaratives Datenformat für das Steuerrecht ausprobiert, wird die Behandlung beliebig verschachtelter Ausdrücke sehr umständlich
- Da die einzige zusammengesetzte Datenstruktur in JSON das Objekt ist, muss jedes Kindobjekt seinen Typ über
"type", "kind" usw. selbst angeben
- In XML zeigt der Tag-Name selbst den Typ des Objekts an, daher ist keine zusätzliche Deklaration nötig
- Die JSON-Darstellung desselben Fakts
/tentativeTaxNetNonRefundableCredits ist sogar länger und komplexer als die XML-Version
- XML unterstützt Kommentare (comments), eine vernünftige Behandlung von Leerraum und Zeilenumbrüchen und vermeidet damit Unannehmlichkeiten, die bei JSON als selbstverständlich hingenommen werden
- Attribute und benannte Kindelemente bieten in der Sprachgestaltung Ausdruckskraft, um zu wählen, was hervorgehoben werden soll
- Es ist möglich, eigene Datentypen zu definieren, etwa die Unterscheidung zwischen „Dollar“ und „Integer“
- Bei langen erklärenden Texten ist XML gegenüber JSON deutlich angenehmer zu lesen und manuell zu bearbeiten
Die Universalität von XML und sein Tool-Ökosystem
- Alternative Syntaxen wie S-Expressions, Prolog oder KDL mögen lesbarer sein als XML, doch mit XML bekommt man Parser und ein universelles Tool-Ökosystem kostenlos dazu
- S-Expressions funktionieren gut in Lisp, Prolog-Terme in Prolog, aber XML lässt sich in praktisch jedes Format umwandeln
- In Prolog kann XML mit einem einzigen Prädikat in Prolog-Terme umgewandelt werden
- Erwähnt wird auch die Frage des Hacker-News-Nutzers ok123456, ob man nicht einfach „Prolog/Datalog verwenden“ könne; das sei möglich, aber bei der Allgemeinheit habe XML die Nase vorn
- Zu YAML sagte Chris Given: „Man sollte niemals versuchen, die Logik des US-Steuerrechts in YAML auszudrücken“
- Praktische Beispiele mit XPath: Es wurde ein Skript geschrieben, das mit einer einzigen Shell-Zeile unscharf nach Faktpfaden sucht und die Definition des ausgewählten Pfads sofort anzeigt
cat facts.xml | xpath -q -e '//Fact/@path' | grep -o '/[^"]*' | fzf zum Suchen von Fakten
- Hinzu kam eine Funktion, die die Abhängigkeitskette zurückverfolgt, um festzustellen, welche Fakten von einem bestimmten Fakt abhängen
- Aus rund 60 Zeilen Bash wurde so ein Debugging-Tool, das fast täglich genutzt wird
- Auch Teammitglieder bauten jeweils eigene ähnliche schnelle Debugging-Tools; alle parsten XML mit minimalem Aufwand und arbeiteten in ihren eigenen Sprachen, ohne die Scala-Implementierung von Fact Graph anzufassen
- Die wichtigste Lehre: Universelle Datenrepräsentation ist extrem wertvoll, und in diese Kategorie fallen nur JSON und XML
- In den meisten Fällen sollte man JSON wählen; wenn jedoch ein DSL benötigt wird, ist XML die günstigste Option, und gerade diese Kosteneffizienz erlaubt es Teams, ihr Innovationsbudget an anderer Stelle einzusetzen
Zusatzpunkte
- Auch Nicht-Programmierer können XML lesen, wenn das Schema gut entworfen ist; dennoch ist es wünschenswert, zusätzliche alternative Ansichten bereitzustellen
- In letzter Zeit wächst das Interesse an XML wieder: etwa mit Jake Lows Tool
grex, das XML-Dokumente in eine flache zeilenorientierte Darstellung umwandelt, oder mit Xee, einer modernen in Rust implementierten XPath/XSLT-Engine von Martijn Faassen
- Die Fakten in TWE dienen der Schätzung der Quellensteuer und dürfen nicht direkt für die Steuererklärung verwendet werden
1 Kommentare
Hacker-News-Kommentare
XML ist ein teures Format, wenn man es in mehreren Sprachen korrekt parsen will.
Um es tatsächlich annähernd standardkonform zu implementieren, ist man meist auf drei Open-Source-Implementierungen wie libxml2, expat und Xerces angewiesen.
Der Kern von SGML-artigen Sprachen ist, dass „Listen“ als Objekte erster Klasse und Verschachtelung als Objekte zweiter Klasse behandelt werden; zusätzlich lässt sich Metadaten über zwei Achsen hinzufügen: Tag-Namen und Attribute.
XML ist als DSL weiterhin nützlich, aber wenn man echtes XML verwenden will, sollte man das Wort „cheap“ fallen lassen.
Außerdem kann man eine deklarative DSL so gestalten, dass sie wie eine imperative Formel aussieht. Ein Ausdruck wie
totalOwed = totalTax - totalPaymentskann zum Beispiel dieselbe Bedeutung haben wie eine XML-DSL.Sprachen wie METAFONT zeigen diesen Ansatz (Beispiellink).
Ich sehe oft, dass XML immer wieder dieselben Fehler macht.
Die einfache Wahrheit je mehr Funktionen ein Format hat, desto schwerer lässt es sich parsen wird häufig vergessen.
JSON ist beliebt, weil es wenige Funktionen hat und deshalb leicht zu parsen ist.
XML dagegen hat zu viel hineingepackt: attributes, namespaces, CDATA, DTDs und mehr.
Es gab auch Diskussionen, SQLite als Austauschformat zu verwenden, aber auch das könnte wie XML in Komplexität ausarten.
Dass CSV trotz allem weiter beliebt ist, liegt ebenfalls an dieser Einfachheit.
Heutige Versuche, JSON gewaltsam mit Kommentaren oder Typinformationen anzureichern, sind eine Neuauflage schlechter XML-Eigenschaften.
Als Autor stimme ich zu.
Eine deklarative Spezifikation wie eine mathematische Formel aussehen zu lassen, ist möglich, aber am Ende erschafft man damit eine neue Sprache.
Dann steht man vor dem Problem, den Parser in jede Umgebung portieren zu müssen.
Man muss auch syntaktische Entscheidungen wie Operatorpriorität oder switch-Ausdrücke selbst treffen, und die Komplexität explodiert schnell.
Genau deshalb habe ich das Wort „cheap“ verwendet — Kosten spart man, wenn man ein Format nutzt, für das es bereits in allen Umgebungen Parser und Tooling gibt.
Die Ausdruckskraft sinkt zwar, aber für kleine Teams ist das eine kluge Wahl.
Ich habe in Enterprise-Java viel mit XML gearbeitet, und es war eine Hauptursache für Speicher- und CPU-Engpässe.
XML ist ganz sicher nicht cheap.
Der Kern von SGML ist das regex-basierte Inhaltsmodell von Elementen.
Es geht nicht nur um Listenstrukturen; man kann auch grammatische Produktionsregeln wie in BNF definieren.
Die Formulierung „XML proper“ statt „XML lookalike“ wirkt übertrieben pedantisch.
Auch wenn man nicht alle XML-Funktionen nutzt, bleibt es XML.
Das ist so, als würde man einen Schulbus ohne Getränkehalter als „Bus-Imitat“ bezeichnen.
Ich denke, man sollte statt XML einfach eine Sprache mit guter eDSL-Unterstützung verwenden.
Haskell, OCaml und Scala können mit Konzepten wie applicative oder arrow parallele Berechnungen leicht ausdrücken.
Auch in JavaScript kann man statt
.reduce()eine Abstraktion wiesumbauen.Wenn man eine XML-DSL entwirft, muss man Probleme wie Parallelisierung, Lesbarkeit und das Erfinden neuer Syntax letztlich erneut lösen.
In komplexen Domänen stößt man dabei leicht auf Greenspuns zehntes Gesetz.
Das Problem bei Sprachen wie Haskell ist allerdings, dass sie schwer zu lernen sind.
Selbst Entwickler mit 30 Jahren Berufserfahrung empfinden die Einstiegshürde oft als hoch.
Raku ist ebenfalls eine gute Wahl.
Es begann auf Haskell-Basis und unterstützt eingebaute Grammars und einen funktionalen Stil, was das Schreiben von DSLs begünstigt.
HTML! (kurze scherzhafte Reaktion)
Auch Lisp wäre möglich.
An S-Expressions sieht man sofort, wie wortreich und schwergewichtig XML wirkt.
Man könnte JSON-Strukturen besser gestalten.
Wenn jeder Knoten aus genau einem Typ-Schlüssel und einem Array-Wert besteht, lässt sich das ähnlich wie ein S-Expression ausdrücken.
Damit wäre Streaming-Parsing möglich, und man kennt den Typ im Voraus.
Das ist bei großen Datensätzen nützlich.
JSON ist viel einfacher als XML und hat geringere Parsing-Kosten.
Bei XML ist die Zustandsverwaltung durch Tag-Paare, Attributverarbeitung usw. komplex,
während man bei JSON nur
{}und[]korrekt zuordnen muss.Diese Einfachheit summiert sich und führt zu geringerer Latenz.
Andererseits gibt es in JSON so viele Anführungszeichen, dass es sich wie visuelles Rauschen anfühlt.
Persönlich finde ich EDN aus Clojure sauberer.
So eine JSON-Struktur wirkt ästhetisch wie eine degenerierte Form.
Wenn Daten Tags brauchen, sollte man dafür besser eine passendere Darstellungsform verwenden.
Der Artikel The Lost Art of XML war interessanter.
Besonders einprägsam fand ich die Sichtweise, dass ein großer Teil der Webentwicklungswerkzeuge als Folge der Niederlage von XML in den Browserkriegen entstanden ist.
Der Behauptung „XML wurde aufgegeben, weil JavaScript gewonnen hat“ kann ich aber schwer zustimmen.
Browser unterstützten XML ursprünglich durchaus (das X in AJAX steht für XML).
Entwickler mochten XML einfach nicht.
Ich denke, XML wurde wegen Überdesign und Komplexität gemieden.
Ich habe die Zeit der alten XML-APIs selbst erlebt, und XML war wirklich schmerzhaft.
Für jede Sprache musste man eigene Encoder/Decoder bauen, und die Wartung war mühsam.
JSON wird einfach auf Arrays und Objekte abgebildet und bietet deshalb eine hervorragende Sprachinteroperabilität.
Wenn ich an die vergeudete Zeit in XML-Schema-Meetings denke, hat JSON das API-Design vereinfacht wie Prettier die Debatte über Tabs vs. Spaces beendet hat.
Am Ende beginnt es mit der Haltung „Ich will nichts Kompliziertes lernen“, aber mit der Zeit taucht dann doch wieder Bedarf an zusätzlichen Funktionen auf.
Die polnischen Steuerbehörden lieben XML.
Aber ihr XML ist so kryptisch, dass es für Menschen kaum lesbar ist.
Feldnamen sehen etwa wie
P_19Naus, und um die tatsächliche Bedeutung zu verstehen, muss man das Schema nachschlagen.Sogar Nummern von Gesetzesartikeln sind darin enthalten.
Ironischerweise arbeitet der Verfasser des Umsatzsteuergesetzes heute als Steuerberater.
Ich nutze selbst eine auf S-Expressions basierende DSL.
Sie übernimmt in einer WebAssembly-basierten Desktop-Browser-Runtime die Rolle von HTML und CSS
und wird auch für eine eigene Markup-Sprache wiederverwendet, die Probleme bei der Dokumentsynchronisation löst.
Beispiele dazu gibt es im CanvasUI-Beispielcode, in der Style-Datei und im Dokumentationstool.
Die Reaktion von Bewerbern, wenn sie plötzlich selbst eine kleine Sprache implementieren, war beeindruckend.
XML ist weniger eine DSL als vielmehr ein allgemeines Parser-/Lexer-Werkzeug.
Es wandelt Text nur in einen AST um; die eigentliche DSL ist die Spezifikation, die darauf aufbaut.
Es ist funktionsreich und komplex, hat aber den Vorteil eines reichhaltigen Tooling-Ökosystems.
Für erzeugten Text ist es besser geeignet als für manuell geschriebene Inhalte.
Mit eingebauter XSD-Schemavalidierung lässt sich die Konsistenz eines Dokuments sofort prüfen.
Sich über XML zu beschweren, ohne Automatisierungswerkzeuge zu nutzen, ist so, als würde man Binärdaten ohne Disassembler bearbeiten.
Aber Schemaprüfung allein kann nicht die inhaltliche Korrektheit garantieren.
Das ist dasselbe Prinzip wie bei Type Checking, das ebenfalls keine Programmkorrektheit garantiert.
XSD ist nützlich, aber komplex und mit vielen Einschränkungen verbunden.
Deshalb sind Teile der XML-Community zu RELAX-NG gewechselt, ohne dass es XSD vollständig ersetzt hätte.
Ich frage mich, welche Aufgaben wirklich zwingend eine XSD-Validierung brauchen.
XML ist als Markup-Sprache in Ordnung und auch als Datenaustauschformat brauchbar, aber als Programmiersprache ist es schrecklich.
Für JSON gilt Ähnliches: gut für Datenaustausch, aber als Sprache bereut man es schnell.
YAML-basierte Sprachen wie Ansible sind ein Beispiel dafür.
Dagegen haben sich Lisp-S-Expressions trotz einer JSON-ähnlichen Struktur zu hervorragenden Sprachen entwickelt.
Das Problem bei XML ist weniger XML selbst als vielmehr, dass gutes XML schwer zu erzeugen ist.
Der Standard ist komplex, und jeder Produzent stellt Dinge anders dar, was die Konsistenz verschlechtert.
Bei JSON ist diese Streuung deutlich kleiner.
Wenn man XML von Finanzinstituten sieht, kann einen das regelrecht verzweifeln lassen.
Das Problem bei XML lag letztlich eher an langsamem Tooling und unvollständigen Validatoren.
Größere Engpässe waren die Qualität der Werkzeuge, nicht die Komplexität der Datenrepräsentation.
Eigentlich war das Problem weniger „gutes XML“ als dass man zu leicht grottiges XML erzeugen konnte.
Deshalb hat die Community mit namespaces, Validierung, Transformation und Semantic Web versucht, Interoperabilität herzustellen.
Das war ein Kompromiss, um in einer Umgebung weiterarbeiten zu können, in der vollständiger Konsens unmöglich ist.