10 điểm bởi GN⁺ 2026-03-15 | 1 bình luận | Chia sẻ qua WhatsApp
  • Sở Thuế vụ Hoa Kỳ đã công bố mã nguồn mở Tax Withholding Estimator (TWE) mới, với nguyên tắc thiết kế cốt lõi là mô hình hóa luật thuế Mỹ bằng đặc tả khai báo dựa trên XML
  • Logic tính thuế của TWE được xây dựng trên một công cụ logic tên là Fact Graph, biểu diễn từng hạng mục thuế dưới dạng đồ thị phụ thuộc của các "fact" được định nghĩa bằng XML
  • Nếu triển khai logic thuế bằng ngôn ngữ mệnh lệnh như JavaScript, sẽ phát sinh các vấn đề như quản lý thứ tự thực thi, mất các giá trị trung gian và lộ chi tiết cài đặt, nên cách tiếp cận khai báo là điều thiết yếu
  • JSON không phù hợp để xử lý biểu thức lồng nhau tùy ý, còn XML thì bản thân thẻ đã thể hiện loại đối tượng, nên thuận lợi hơn nhiều cho việc xây dựng DSL
  • XML cho phép tận dụng miễn phí hệ sinh thái công cụ trưởng thành như XPath, khiến nó trở thành lựa chọn tiết kiệm chi phí nhất cho đặc tả khai báo đa nền tảng

Fact Graph: luật thuế Mỹ được biểu diễn bằng XML

  • Tax Withholding Estimator (TWE) do IRS công bố là công cụ giúp người nộp thuế nhập thu nhập và các khoản khấu trừ để ước tính số thuế và mức khấu lưu
    • Dự án được công khai dưới dạng mã nguồn mở và cũng chấp nhận đóng góp từ cộng đồng
  • TWE là một trang tĩnh được tạo từ hai cấu hình XML, trong đó cấu hình đầu tiên là Fact Dictionary biểu diễn luật thuế Mỹ
  • Fact Graph là công cụ logic ban đầu được xây dựng cho dự án IRS Direct File, dùng để tính nghĩa vụ thuế và khoản khấu lưu của người nộp thuế dựa trên các fact được định nghĩa trong Fact Dictionary
  • Mỗi fact được định nghĩa bằng XML; ví dụ, /totalOwed được biểu diễn như một fact suy diễn (Derived) lấy /totalTax trừ đi /totalPayments
    • "Tổng phải nộp (total owed)" là phần chênh lệch giữa tổng thuế trên thu nhập (total tax) và số tiền đã nộp trước đó (total payments)
  • Khoản tín dụng được hoàn lại (refundable credits) là các khoản tín dụng thuế có thể khiến số dư thuế trở thành âm, như Earned Income Credit, Child Tax Credit, American Opportunity Credit..., được cộng lại bằng <Add>
  • Khoản tín dụng không được hoàn lại (non-refundable credits) chỉ có thể giảm gánh nặng thuế xuống đến 0; toán tử <GreaterOf> được dùng để chọn giá trị lớn hơn giữa 0 và (thuế tạm tính - tín dụng không được hoàn lại)
  • Giá trị do người dùng nhập dùng thẻ <Writable> thay vì <Derived>, và chỉ định kiểu giá trị bằng <Dollar/>, <Boolean/>...
  • Các fact phụ thuộc lẫn nhau, tạo thành một cấu trúc đồ thị để suy ra con số thuế cuối cùng

Vì sao logic thuế cần đặc tả khai báo

  • Nếu viết cùng phép tính đó bằng JavaScript, câu lệnh như const totalOwed = totalTax - totalPayments có vẻ ngắn gọn, nhưng đó là cách làm mệnh lệnh (imperative), thực thi tuần tự và làm mất các bước trung gian
  • Khi quan hệ phụ thuộc trở nên sâu hơn, sẽ xuất hiện vấn đề thứ tự thực thi: một hàm nhập liệu người dùng như getInput() có thể chặn toàn bộ phép tính phía sau; thậm chí câu hỏi cần hỏi cũng phải thay đổi tùy theo việc có vợ/chồng hay không
  • Trong logic cộng gộp thu nhập Social Security, các chi tiết cài đặt JavaScript như map/reduce bị lộ ra ngoài, còn <CollectionSum> trong XML lại biểu đạt trực tiếp chính khái niệm toán học của thuế
    • Dùng <Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/> để cộng các mục trong một tập hợp
  • Fact Dictionary hoạt động theo kiểu khai báo (declarative): chỉ cần mô tả phép tính được đặt tên và các quan hệ phụ thuộc, không cần mô tả bước thực thi cụ thể hay thứ tự, còn công cụ sẽ tự quyết định cách chạy
  • Lợi ích quan trọng nhất của mô hình thuế khai báo là khả năng kiểm toán (auditability)tự soi chiếu nội bộ (introspection): có thể hỏi chương trình "con số này được suy ra như thế nào"
    • Chương trình mệnh lệnh thường đã loại bỏ các giá trị trung gian, nên chỉ có thể kiểm tra qua log hoặc debugger; với luật thuế Mỹ có hàng trăm phép tính trung gian thì cách đó không thể mở rộng
  • Theo Chris Given, tác giả gốc của Fact Graph, Fact Graph là phương tiện để chứng minh rằng những mục chưa được hỏi không làm thay đổi kết quả khai thuế, đồng thời người dùng đang nhận đầy đủ mọi ưu đãi thuế mà họ đủ điều kiện hưởng
  • Intuit, công ty làm ra TurboTax, cũng đi đến kết luận tương tự và đã công bố whitepaper "Tax Knowledge Graph" vào năm 2020, nhưng phần triển khai không được mở công khai
    • Fact Graph của IRS là mã nguồn mở và thuộc public domain, nên bất kỳ ai cũng có thể nghiên cứu, chia sẻ và mở rộng

Vì sao XML phù hợp với DSL hơn nhiều so với JSON

  • Khi thử dùng JSON làm định dạng biểu diễn dữ liệu khai báo cho luật thuế, việc xử lý biểu thức lồng nhau tùy ý trở nên rất bất tiện
    • Vì cấu trúc dữ liệu tổng hợp duy nhất của JSON là object, nên mọi object con đều phải tự chỉ rõ loại của mình bằng các trường như "type", "kind"...
    • Trong XML, tên thẻ tự thân đã biểu thị loại đối tượng, nên không cần khai báo riêng
  • Cách biểu diễn fact /tentativeTaxNetNonRefundableCredits bằng JSON thậm chí còn dài và phức tạp hơn XML
  • XML hỗ trợ comment, xử lý khoảng trắng/xuống dòng hợp lý, nên không có những bất tiện thường thấy ở JSON
  • Attribute và các phần tử con có tên rõ ràng mang lại khả năng biểu đạt để lựa chọn điều gì cần được nhấn mạnh trong thiết kế ngôn ngữ
  • Có thể định nghĩa kiểu dữ liệu riêng biệt như phân biệt giữa "đô la" và "số nguyên"
  • Khi phải xử lý các đoạn mô tả văn bản dài, XML dễ đọc và dễ chỉnh sửa thủ công hơn nhiều so với JSON

Tính phổ quát và hệ sinh thái công cụ của XML

  • Các cú pháp thay thế như S-expression, Prolog, KDL có thể dễ đọc hơn XML, nhưng dùng XML sẽ có ngay parser và hệ sinh thái công cụ phổ dụng miễn phí
    • S-expression hoạt động tốt trong Lisp, term Prolog hoạt động tốt trong Prolog, còn XML thì có thể chuyển đổi sang bất kỳ định dạng nào
  • Trong Prolog, việc chuyển XML sang term Prolog có thể thực hiện chỉ bằng một predicate duy nhất
  • Bài cũng nhắc đến câu hỏi của người dùng Hacker News ok123456 rằng liệu có thể dùng "Prolog/Datalog" hay không; câu trả lời là có thể, nhưng XML nhỉnh hơn về tính phổ dụng
  • Về YAML, Chris Given nói rằng: đừng bao giờ cố biểu diễn logic luật thuế Mỹ bằng YAML
  • Ví dụ thực tế với XPath: tác giả viết script chỉ bằng một dòng lệnh shell để fuzzy-search đường dẫn fact và truy vấn ngay định nghĩa của đường dẫn được chọn
    • cat facts.xml | xpath -q -e '//Fact/@path' | grep -o '/[^"]*' | fzf để tìm fact
    • Cũng bổ sung chức năng lần ngược chuỗi phụ thuộc để theo dõi fact nào đang phụ thuộc vào fact đó
    • Một script bash khoảng 60 dòng đã phát triển thành công cụ debug gần như dùng hằng ngày
  • Các thành viên khác trong nhóm cũng tự tạo các công cụ debug nhanh tương tự, đều chỉ parse XML ở mức tối thiểu và làm việc bằng ngôn ngữ riêng của mình mà không cần đụng vào phần cài đặt Scala của Fact Graph
  • Bài học cốt lõi: biểu diễn dữ liệu phổ dụng có giá trị rất lớn, và trong nhóm này thực chất chỉ có JSON và XML
    • Trong đa số trường hợp nên chọn JSON, nhưng khi cần DSL thì XML là lựa chọn rẻ nhất, và nhờ hiệu quả chi phí đó, nhóm có thể dành ngân sách đổi mới cho những nơi khác

Thông tin bổ sung

  • Ngay cả những người không phải lập trình viên cũng có thể đọc XML nếu schema được thiết kế tốt, dù vậy nên xây dựng thêm các cách hiển thị thay thế
  • Gần đây đang có xu hướng quan tâm trở lại đến XML: công cụ grex của Jake Low dùng để chuyển tài liệu XML sang dạng phẳng theo dòng, và Xee của Martijn Faassen là engine XPath/XSLT hiện đại viết bằng Rust
  • Các fact trong TWE phục vụ cho việc ước tính khấu lưu, vì vậy không nên dùng trực tiếp cho việc khai thuế

1 bình luận

 
GN⁺ 2026-03-15
Bình luận trên Hacker News
  • XML là một định dạng tốn kém nếu muốn parse cho đúng đắn trên nhiều ngôn ngữ
    Trên thực tế, để triển khai gần chuẩn thì phải dựa vào ba bộ hiện thực mã nguồn mở như libxml2, expat và Xerces
    Cốt lõi của họ ngôn ngữ SGML là coi “danh sách” như đối tượng hạng nhất, còn lồng nhau là đối tượng hạng hai, và có thể thêm metadata theo hai trục là tên thẻ và thuộc tính
    XML vẫn hữu ích với vai trò DSL, nhưng nếu dùng XML đúng nghĩa thì nên bỏ từ “cheap” đi
    Ngoài ra, cũng có thể làm cho DSL khai báo trông giống công thức mệnh lệnh. Ví dụ, biểu thức như totalOwed = totalTax - totalPayments có thể mang cùng ý nghĩa với một XML DSL
    Những ngôn ngữ như METAFONT cho thấy cách tiếp cận này (liên kết ví dụ)

    • Tôi thường thấy XML lặp đi lặp lại cùng một sai lầm
      Người ta hay quên chân lý đơn giản rằng càng nhồi nhiều tính năng vào định dạng thì càng khó parse
      JSON phổ biến vì ít tính năng nên dễ parse
      Trong khi đó XML nhét vào quá nhiều thứ như attributes, namespaces, CDATA, DTDs
      Cũng từng có tranh luận về việc dùng SQLite làm định dạng trao đổi, nhưng nó cũng có nguy cơ trở nên phức tạp như XML
      Đây cũng là lý do CSV vẫn được yêu thích vì tính đơn giản
      Những nỗ lực ngày nay nhằm nhét comment hay thông tin kiểu vào JSON thực chất là tái hiện các thuộc tính tệ của XML

    • Với tư cách tác giả bài viết, tôi đồng ý
      Có thể làm cho đặc tả khai báo trông như biểu thức toán học, nhưng rốt cuộc đó vẫn là tạo ra một ngôn ngữ mới
      Khi đó sẽ phát sinh vấn đề phải port parser sang mọi môi trường
      Bạn cũng phải tự quyết định các chi tiết cú pháp như độ ưu tiên toán tử hay biểu thức switch, và rồi độ phức tạp bùng nổ
      Đó chính là lý do tôi dùng từ “cheap” — dùng một định dạng vốn đã có parser và tooling trên mọi môi trường giúp tiết kiệm chi phí
      Dù giảm bớt sức biểu đạt, đây vẫn là lựa chọn khôn ngoan cho các nhóm nhỏ

    • Tôi từng dùng XML rất nhiều trong Java enterprise, và nó là thủ phạm chính gây nghẽn bộ nhớ và CPU
      XML hoàn toàn không hề cheap

    • Điểm cốt lõi của SGML là mô hình nội dung dựa trên biểu thức chính quy của các phần tử
      Không chỉ là cấu trúc danh sách, nó còn có thể định nghĩa các quy tắc sinh ngữ pháp như BNF

    • Cách gọi “XML proper” thay vì “XML lookalike” nghe có vẻ quá soi mói
      Dù không dùng mọi tính năng của XML thì nó vẫn là XML
      Cũng như gọi xe buýt trường học là “đồ giả xe buýt” chỉ vì nó không có chỗ để cốc vậy

  • Tôi nghĩ nên dùng ngôn ngữ hỗ trợ eDSL tốt thay vì XML
    Những ngôn ngữ như Haskell, OCaml, Scala có thể biểu đạt tính toán song song dễ dàng bằng các khái niệm như applicative hay arrow
    Trong JavaScript cũng có thể tạo các trừu tượng như sum thay vì .reduce()
    Nếu tạo XML DSL thì rốt cuộc lại phải giải lại các bài toán như song song hóa, tính dễ đọc và phát minh cú pháp mới
    Trong miền bài toán phức tạp, rất dễ đụng phải định luật thứ 10 của Greenspun

    • Nhưng vấn đề của những ngôn ngữ như Haskell là rất khó học
      Ngay cả lập trình viên có 30 năm kinh nghiệm cũng cảm thấy rào cản gia nhập rất cao

    • Raku cũng là một lựa chọn tốt
      Nó khởi đầu từ nền tảng Haskell và hỗ trợ Grammar tích hợp cùng phong cách hàm nên thuận lợi để viết DSL

    • HTML! (một phản hồi ngắn mang tính đùa vui)

    • Lisp cũng làm được
      Chỉ cần nhìn S-expression là thấy ngay XML dài dòng và nặng nề đến mức nào

  • Có thể thiết kế cấu trúc JSON tốt hơn một chút
    Nếu mỗi node gồm một khóa kiểu và một giá trị mảng thì có thể biểu diễn giống S-expression
    Cách này cho phép streaming parse và biết kiểu trước
    Nó hữu ích với các bộ dữ liệu lớn

    • JSON đơn giản hơn XML rất nhiều và chi phí parse thấp hơn
      XML đòi hỏi quản lý trạng thái phức tạp như khớp cặp thẻ, xử lý thuộc tính
      Còn JSON chỉ cần khớp {}, []
      Sự đơn giản này cộng dồn lại thành độ trễ thấp hơn

    • Nhưng JSON có quá nhiều dấu ngoặc kép nên tạo cảm giác như nhiễu thị giác
      Cá nhân tôi thấy EDN của Clojure gọn gàng hơn

    • Kiểu cấu trúc JSON này khiến tôi thấy như một dạng thoái hóa về mặt thẩm mỹ
      Nếu dữ liệu cần thẻ thì tốt hơn nên dùng cách biểu diễn phù hợp với nó

  • Tôi thấy bài The Lost Art of XML còn thú vị hơn
    Góc nhìn cho rằng phần lớn công cụ phát triển web ra đời từ việc XML thua trong cuộc chiến trình duyệt là điều khá ấn tượng

    • Nhưng tôi khó đồng ý với lập luận rằng “XML bị bỏ rơi vì JavaScript đã thắng”
      Trình duyệt vốn đã hỗ trợ XML từ đầu (chữ X trong AJAX là XML)
      Chỉ là lập trình viên không thích XML mà thôi
      Tôi nghĩ XML bị quay lưng vì thiết kế quá mức và độ phức tạp

    • Với tư cách người từng trải qua thời kỳ API XML, XML thực sự rất đau khổ
      Mỗi ngôn ngữ đều phải tự tạo encoder/decoder riêng và việc bảo trì cũng rất vất vả
      JSON đơn giản chỉ ánh xạ sang mảng và object nên khả năng tương thích giữa các ngôn ngữ rất tốt
      Nghĩ lại thời từng lãng phí hàng giờ trong các cuộc họp thiết kế XML schema, JSON đã đơn giản hóa việc thiết kế API giống như Prettier chấm dứt tranh cãi tab vs space

    • Cuối cùng đây là một mẫu hình bắt đầu từ thái độ “không muốn học thứ phức tạp”, nhưng theo thời gian rồi lại cần thêm tính năng

  • Cơ quan thuế Ba Lan rất yêu XML
    Nhưng XML của họ khó hiểu đến mức con người không thể đọc nổi
    Tên field kiểu như P_19N, và phải xem schema mới biết ý nghĩa thật sự
    Thậm chí còn chứa cả số điều luật
    Trớ trêu thay, người từng soạn luật VAT hiện đang làm tư vấn thuế

  • Tôi đang trực tiếp dùng một DSL dựa trên S-expression
    Nó đóng vai trò như HTML và CSS trong một runtime trình duyệt desktop dựa trên WebAssembly,
    đồng thời còn được tái sử dụng trong ngôn ngữ markup riêng để giải quyết vấn đề đồng bộ tài liệu
    Có thể xem ví dụ liên quan ở mã ví dụ CanvasUI, file stylecông cụ tài liệu hóa

    • S-expression có parser cực kỳ đơn giản để hiện thực, đến mức tôi từng dùng nó làm bài toán phỏng vấn
      Phản ứng của ứng viên khi tự tay hiện thực được một ngôn ngữ đơn giản thật sự rất ấn tượng
  • XML không hẳn là DSL mà giống một công cụ parser/lexer tổng quát hơn
    Nó chỉ chuyển văn bản thành AST; DSL thực sự là đặc tả được định nghĩa trên đó
    Nó nhiều tính năng và phức tạp, nhưng đổi lại có hệ sinh thái tooling phong phú
    Nó phù hợp để xử lý văn bản được sinh tự động hơn là viết thủ công

  • Vì có tích hợp sẵn kiểm tra schema XSD nên có thể xác minh tính nhất quán của tài liệu ngay lập tức
    Phàn nàn rằng XML khó dùng trong khi không dùng công cụ tự động hóa cũng giống như xử lý binary mà không có disassembler

    • Nhưng chỉ kiểm tra schema thôi thì không thể đảm bảo nội dung là đúng
      Cũng giống như type checking không thể đảm bảo chương trình là chính xác

    • XSD hữu ích nhưng phức tạp và nhiều ràng buộc
      Vì vậy một số cộng đồng XML đã chuyển sang RELAX-NG, dù nó chưa thể thay thế hoàn toàn

    • Tôi tò mò không biết những công việc nào thực sự bắt buộc phải có kiểm tra XSD

  • XML ổn khi là ngôn ngữ markup, cũng dùng được làm định dạng trao đổi dữ liệu, nhưng rất kinh khủng nếu dùng làm ngôn ngữ lập trình
    JSON cũng tương tự: tốt cho trao đổi dữ liệu, nhưng dùng làm ngôn ngữ thì sẽ hối hận
    Các ngôn ngữ dựa trên YAML như Ansible là ví dụ điển hình
    Trong khi đó, S-expression của Lisp có cấu trúc gần JSON nhưng lại phát triển thành một ngôn ngữ xuất sắc

  • Vấn đề của XML không hẳn nằm ở bản thân XML mà ở chỗ rất khó tạo ra XML tốt
    Tiêu chuẩn phức tạp, và mỗi nhà sản xuất lại biểu diễn khác nhau nên thiếu tính nhất quán
    JSON có độ lệch chuẩn này nhỏ hơn nhiều
    Nhìn XML của các tổ chức tài chính thôi cũng đủ thấy tuyệt vọng

    • Vấn đề của XML rốt cuộc là do tooling chậm và validator không hoàn chỉnh
      Chất lượng công cụ mới là nút thắt lớn hơn độ phức tạp của cách biểu diễn dữ liệu

    • Thực ra vấn đề không phải “XML tốt” mà là người ta có thể tạo ra XML tệ hại quá dễ dàng
      Vì vậy cộng đồng mới cố gắng đảm bảo khả năng tương tác bằng namespaces, validation, transformation, Semantic Web và các thứ tương tự
      Đó là một thỏa hiệp để vẫn có thể làm việc trong môi trường không thể đạt được sự đồng thuận hoàn hảo