Tax logic evaluation with Prolog · mthom scryer-prolog · Discussion #3287

6 min read Original article ↗

For a couple weeks I've been working on a Prolog implementation of the US Internal Revenue Service's Fact Graph library, which is used to model US Tax Law and calculate tax obligation based on user input. I have a draft of this implementation, factgraph.pl, that I'd like to share here! (This is my personal work, not anything official.)

Conceptually, it's an interpreter. I took a lot of inspiration from lisprolog.pl in the structure. It parses the Fact Dictionary (the logic specification) and the fact graph (the user's inputs, slightly confusing overloading of the term) into in-memory structures, from which additional facts can be derived. For instance, the user might input two jobs with different incomes, and the /totalIncome fact would evaluate to the sum of the incomes of those jobs. I wrote a basic introduction to this schema on my blog.

Prolog is a nice fit for this problem because it gives me a lot more confidence in the thoroughness of my specification that if I had attempted to re-implement it in an imperative language.

A couple things to highlight:

  • It's only one file, currently <450 lines. I expect that I can get to a complete implementation under 600.
  • It uses both XML and JSON parsing.
  • It uses a lot of reifed monotonic logic. The evaluation model is regrettably non-monotonic. I'm not sure if this is even possible to fix with the current FG semantics, which allow testing for incompleteness. The monotonic subsets of the codebase, however, are much easier to reason about and test as a result.
  • There are a lot of improvements I'd like to make to the testing, but you can kind of see where I'm going with it in test.pl.

A couple things I might need some help with:

  • Is there a blessed date math solution? I have been putting that off because I expect it to be frustrating.
  • Tests that actually tell me which of the subclauses failed. I know there's a lot of work in progress on testing, hoping my hacky attempt can be of some use in that effort.
  • You can't actually run this with Scryer 0.10 because the XML library is broken, and it would be lovely to release the fix.

Anyway, I'm not looking for anything in particular from the community, just wanted to share a practical application that's helping solve a real problem I have, and a basis for some questions I might be asking the future.

You must be logged in to vote

Hi Alex

I will leave a short comment here.
First of all I want to applaud your effort. Prolog really is the correct choice for such an endeavor. So cheers for taking law as code so seriously.
Secondly, I am a founder (together with Bryan who is also in the Prolog community) of VATmiraal. We are a VAT engine provider taking this to a professional level. In the past few months we have learned a lot about Prolog and its unique place in the pantheon of programming languages and I can now confidently say our project would be completely not feasible without Prolog. Our main codebase had reached +3000 lines and this is only the beginning. It will dramatically expand when we carry out our roadmap (don't want to spill too much publicly).
If you ever want to share experiences or thoughts, let me know.

Regards Jan

You must be logged in to vote

1 reply

@alexpetros

Thanks Jan! It's really nice to know that there are other people tacking this problem space with some similar ideas. Would love to know more when you're ready to share. Best way to contact me is via email, which is on my website.

Very interesting, thank you a lot for sharing this!

Two nonterminals may be useful for factgraph.pl:

element(N, Cs)     --> [element(N,_,Cs)].
element(N, As, Cs) --> [element(N,As,Cs)].

With these nonterminals, we can for example write:

exp(add(Es))          --> element('Add', Cs), ... .

In a few places, operator notation may make the arguments more readable:

eval(L0 < R0, V)        --> eval(L0, L), eval(R0, R), ... .

Please keep up the great work, and if possible, please keep us posted!

You must be logged in to vote

2 replies

@alexpetros

I will incorporate those—the element one especially is really nice. Thank you for reading the source!

@triska

Also you will likely see other opportunities for similar improvements, and also going beyond them for instance by seeing opportunities for term_expansion/2 to generate rules automatically from smaller tailor-made descriptions. This is often the most interesting aspect when programming in Prolog, to find the building blocks, language elements that help us talk about the problem, and beyond that: help us talk about the program that solves the problem.

A key design question is often: Where is the border, where a description-of-the-program becomes harder to understand than a partially evaluated version of the description, i.e., a more direct encoding of the program as Prolog clauses.

Hey @alexpetros,
Regarding your date issue: I have implemented the predicate timestamp_dt/7, which is based on CLP(Z) and allows bidirectional conversion between dates and timestamps. This makes calculations much easier. I am also considering adding additional predicates for further computations, although timestamp_dt/7 can already be used effectively for this purpose.
So far, I have been running extensive tests, and more than 30 million tests have completed successfully without any errors. (but more will follow)
If you need the current version (which will work probably - 30 million succsessfull tests) to move on with your work - pleasea reply below so that we can get in private contact:)
Best regards,
Mike

You must be logged in to vote

1 reply

@alexpetros

Hey this sounds really cool! I would definitely be interested in making use of it, but you would have to be okay with me including it in the source or linking to it somehow, because the codebase is public and I'd like people to be able to use it. I only need to date arithmetic too, in case that changes things. My email is on my website if you'd like to get in touch.

You must be logged in to vote

0 replies