Introduction

Welcome to the Catala domain-specific programming language, an introductory book about Catala. The Catala domain-specific language lets you annotate a legal specification with executable and deployable code in an efficient, high-assurance fashion. Automating the enforcement of a law or regulation through the use of a computer program is not neutral for the Rule of Law as well as the rights of users. Please thread lightly when considering automating anything else than tax or social benefits computation.

Contrary to a rules engine, Catala is a fully-fledged programming language with modular abstractions inspired by functional programming. But most importantly, it enables real collaboration between lawyers and computer scientists by allowing the practice of pair programming through literate programming for writing and updating the programs and their legal specifications.

Who Catala is for

Catala is a domain-specific programming language, hence its use is targeted to a specific class of users and organizations.

Multi-disciplinary teams

Catala is designed to be used by teams mixing legal and computer science expertise. Indeed, automating the enforcement of a law or a regulation requires bridging the ambiguity gap of the legal text to a computer program that knows what to do in every possible situation. Bridging this ambiguity gap is one of missions of lawyers, trained in interpreting legal texts and performing legal research to come up with a decision that can be justified withing the framework of the Rule of Law.

If lawyers should be deciding what the program does, they are not trained in programming concepts and abstractions, nor do they know how to write thousands of lines of code in a clean and maintainable fashion. Hence, the role of the computer scientist is to ensure that the program automating the enforcement of a law or regulation is clean, concise, featuring correctly-scoped abstractions that prevent code duplication and ease maintenance and future changes. Moreover, the computer scientist is also responsible for deploying the program in a bigger IT system that might break its assumptions or stress its performance.

By reusing existing software engineering techniques and tools, while exhibiting lawyer-friendly concepts at its surface, Catala lets lawyers and computer scientists collaborate in a truly agile manner, going beyond the separation between development and quality assurance teams that communicate only via test cases and specification documents.

Government agencies and public service organizations

Automating the enforcement of a law or regulation through a computer program is no small task. To make sure all are treated fairly under the Rule of Law, your program should exhaustively take into account every situation described by the law or regulation. While automating only the most simple situation corresponding to a majority of users might acceptable in certain situations, it cannot be the basis for a production-ready public service. Indeed, creating a difference between an automated “simple path” and a non-automated handling of complex situation widens the digital divide, and increases confusion for users and case workers alike.

Catala shines when the goal is to automate exhaustively and with high assurance a given law or regulation, and to maintain this automation over time. The language and tooling will help you manage the growing complexity, maintenance over legal changes, corner cases and production-ready deployment inside a legacy IT system. If you are looking for a tool to make a first-level user-help chatbot backend that only answers basic questions, or a simplified model of a law for an economic study, then you should consider using other tools. On the other hand, if you are in an organization responsible for running a public service like taxes or social benefits, then you should have the means to properly engineer the computer program responsible for the automation, and use Catala to help you with it.

Because Catala compiles to mainstream programming languages like Python or C, it yields portable source code libraries that are embeddable in virtually any IT system, legacy or modern. More particularly, Catala programs can be deployed behind a Web API or embarked in a desktop application, enabling “write once, use anywhere”. Hence, Catala is particularly suited to historical government agencies that have been operating their IT systems for decades in the past, and will continue to do so decades in the future.

Students and academics in CS and/or Law

Formalizing law, which is a more general term for translating law into executable computer code, has been a subject of scholarly attention for a very long time. AI & Law has historically been the umbrella community for this line of work, with three sequential trends: logic programming, ontologies, and now machine learning and especially natural language processing. While these trends have uncovered important results for formalizing law, big research questions remain. How to model complex legal provisions spanning large corpuses? Can formalizing law be automated? Can legal drafting be technologically augmented? How to detect inconsistencies or loopholes by static or dynamic analysis?

With strong roots to the research community, its commitment to open-source, its formalized semantics and its extensible compiler, Catala as a programming language is an opportunity for student learning and research projects. While teachers and students can use Catala for hands-on exploring of tax and social benefits law, researchers can use Catala programs as datasets or program a new analysis that can be readily deployed to the users of Catala.

If you’re a student, a professor, or anything else actually, you’re welcome to use Catala for free and contribute back to it by filing issues, proposing pull requests, or develop plugins and tooling around it.

Who this book is for

This books is primarily geared towards programmers that want to learn Catala, set up a project using it to translate some legal text into executable code, and be guided through the process. The book assume basic knowledge of functional programming idioms, and generally software engineering experience with another mainstream programming language.

If you, a programmer, work in a multidisciplinary team with one or several lawyers, it is up to you to explain to them what Catala is and how to work with it. This guide will thus also cover various topics around the collaboration between lawyers and programmers.

If you are a lawyer and stumble across this book, you are also welcome to read it, although parts of it will not be relevant to you. You might checkout instead introductory articles that set up the context around computer code, translating law to computer code and introduce the specificities of Catala:

How to use this book

The book is organized into two parts: the user guide and the reference guide. While the user guide is meant to be read linearly and aimed at new users, the reference guide is the go-to stop for checking items about the Catala language and tooling as you’re using it, whether you are a new or experienced user.

The user guide starts with Chapter 1 and the Catala equivalent of “Hello, world“ program. Chapter 2 explains the core concepts of Catala with a hands-on tutorial centered on the automation of a basic, fictional tax system. Moving onto serious business, Chapter 3 dives into the setting up of a real-world Catala project with version control, monitoring legal changes, testing, continuous integration, automated deployment, etc. Finally, Chapter 4 speeds up your learning by answering to (almost) all the questions that you’ll normally stumble upon while coding the law with Catala.

In the reference guide, Chapter 5 details all the features available in the Catala language, while Chapter 6 and Chapter 7 focus on the command line interfaces and features of the two binaries that you will be working with: clerk and catala.

Source code

The source files from which this book is generated can be found on GitHub. While the contents of this book are expected to correspond to the latest version of Catala, some inconsistencies might appear. If you spot one, or have comments or suggestions about the book, please file an issue!

Getting started

Installing Catala on your machine

Catala installation instructions

Disclaimer: Currently, Catala is only available through source building. We plan to package Catala as binaries which will greatly ease the installation process.

Important: during these steps several prompts might occur, choosing the default option (by pressing enter each time) or answering yes (by typing y then enter) is enough.

On Linux/WSL2

For WSL2 users: we assume all the given commands are invoked in a WSL2 environment. WSL2 can be installed by running > wsl --install in a Window’s PowerShell (Windows key + R then type “powershell” in the prompt) which will installed by default a Ubuntu-like virtual machine. Then, you may enter the WSL2 environment by typing “wsl” in the PowerShell.

Getting opam

Install the latest version of opam (version >= 2.2)

With aptitude (debian-like linux distributions):

$ sudo apt update
$ sudo apt install opam

Without aptitude:

$ bash -c “sh <(curl -fsSL https://opam.ocaml.org/install.sh)”

Then, initialize opam:

$ opam init --bare

Getting Catala

Run the following command to install the latest Catala version via opam:

$ opam pin add catala git+https://github.com/CatalaLang/catala -y

Once this finishes, the Catala compiler (and its build system) should be installed. You should be able to succesfully call $ catala --version in your terminal. If that’s not the case, try invoking $ eval $(opam env) priorly.

Getting the LSP server (needed by the VSCode extension)

The VSCode extension requires the Catala’s Language Server Protocol to be installed. This can be done by running:

$ opam pin add catala-lsp git+https://github.com/CatalaLang/catala-language-server -y

Getting the VSCode extension

Install VSCode and open it. Browse the extension marketplace and install the Catala extension.

For WSL2 installations: VSCode needs to reach the installed WSL environment to retrieve the Catala tools which can be done by installing the official WSL VSCode extension (c.f. https://code.visualstudio.com/docs/remote/wsl). Once this is installed, you will need to load a WSL VSCode window by pressing F1 (which opens the VSCode prompt) and execute the following command WSL: Connect to WSL.

Getting the Catala code formatter

Run the following command:

$ opam pin add catala git+https://github.com/CatalaLang/catala-format -y

Note: this installation will take some time as it requires a Rust toolchain.

Once this is installed, you may refresh your VSCode environment (F1 => “Developer: Reload Window”) which will notify the Catala extension that the formatter is now available. You can invoke the formatter using F1 => “Format Document” or by a user-defined’s key-binding.

On Windows (without WSL)

Caution: The Windows installation is currently experimental.

Getting Opam

Open a PowerShell and install opam by invoking

Invoke-Expression “& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) }”

If an unexpected error occurs, follow the opam‘s installation instructions: https://opam.ocaml.org/doc/Install.html

Then, initialize opam:

$ opam init --bare

Getting Catala

Currently, the opam‘s Catala package is not directly buildable on Windows. However, the Catala’s lsp server bundles a subset of Catala which is fine. This may be installed the same way as Linux/WSL2:

$ opam pin add catala git+https://github.com/CatalaLang/catala-lsp -y

Setting up the Catala LSP server

After the previous step, the Catala LSP server should be built in opam’s binaries directory. In order for VSCode to be able to get it, this directory must be added to Windows’ PATH environment variable. The directory in question should be located in %LOCALAPPDATA%\opam\default\bin (n.b., default might be named something else such as “4.14.2”, double-check the directory location).

Getting the VSCode extension

Install VSCode and open it. Browse the extension marketplace and install the Catala extension.

Currently, the code formatter is not yet available on Windows.

Creating your first Catala program

Writing your first Catala code block

Typechecking and running the Catala program

Tutorial : computing your taxes

Welcome to this tutorial, whose objective is to guide you through the features of the Catala language and teach you how to annotate a simple legislative text using the language, and get out an executable program that compute your taxes!

This tutorial does not cover the installation of Catala. For more information about this to the Getting started chapter. If you want follow this tutorial locally, simply create an empty file with the extension .catala_en, which you will be filling as you read the tutorial by copy-pasting the relevant section.

At any point, please refer to the Catala syntax cheat sheet or the reference guide for an exhaustive view of the syntax and features of Catala; this tutorial is rather designed to ease you into the language and its common use patterns.

Mixing law and code

Catala is a language designed around the concept of literate programming, that is the mixing between the computer code and its specification in a single document. Why literate programming? Because it enables a fine-grained correspondance between the specification and the code. Whenever the specification is updated, knowing where to update the code is trivial with literal programming. This is absolutely crucial for enabling long-term maintenance of complex and high-assurance programs like tax or social benefits computation.

Hence, a Catala source code file looks like a regular Markdown document, with the specification written down and styled as Markdown text, with the Catala code only present in well-bounded Catala code blocks introduced by ```catala.

Before writing any Catala code, we must then introduce the specification of the code for this tutorial. This specification will be based on a fictional Tax Code defining a simple income tax. But in general, anything can be used as a specification for a Catala program: laws, executive orders, court cases motivations, legal doctrine, internal instructions, technical specifications, etc. These sources can also be mixed to form a complete Catala program that relies on multiple sources of specification. Concretely, just copy-paste the text of the specification and format it in Markdown syntax inside a Catala source code file.

Without further ado, let us introduce the first bit of specification for our fictional income tax, Article 1 of the CTTC (Catala Tutorial Tax Code):

Article 1

The income tax for an individual is defined as a fixed percentage of the individual’s income over a year.

The spirit of writing code in Catala is to stick to the specification at all times in order to put the code snippets where they belong. Hence, we will introduce below the Catala code snippets that translate Article 1, which should be put just below Article 1 in the Catala source code file.

These code snippets should describe the program that computes the income tax, and contain the rule defining it as a multiplication of the income as rate. It is time to dive into Catala as a programming language.

# We will soon learn what to write here in order to translate the meaning
# of Article 1 into Catala code.

# To create a block of Catala code in your file, bound it with Markdown-style
# “```catala” and “```” delimiters. You can write comments in Catala code blocks
# by prefixing lines with “#”

Setting up data structures

The content of Article 1 assumes a lot of implicit context: there exists an individual with an income, as well as an income tax that the individual has to pay each year. Even if this implicit context is not verbatim in the law, we have to explicit it in the computer code, in the form of data structures and function signatures.

Catala is a strongly-typed, statically compiled language, so all data structures and function signatures have to be explicitly declared. So, we begin by declaring the type information for the individual, the taxpayer that will be the subject of the tax computation. This individual has an income and a number of children, both pieces of information which will be needed for tax purposes :

# The name of the structure, “Individual”, must start with an
# uppercase letter: this is the CamelCase convention.
declaration structure Individual:
  # In this line, “income” is the name of the structure field and
  # “money” is the type of what is stored in that field.
  # Available types include: “integer”, “decimal”, “money”, “date”,
  # “duration”, and any other structure or enumeration that you declare.
  data income content money
  # The field names “income” and “number_of_children” start by a lowercase
  # letter, they follow the snake_case convention.
  data number_of_children content integer

This structure contains two data fields, income and number_of_children. Structures are useful to group together data that goes together. Usually, you get one structure per concrete object on which the law applies (like the individual). It is up to you to decide how to group the data together, but we advise you to aim at optimizing code readability.

Sometimes, the law gives an enumeration of different situations. These enumerations are modeled in Catala using an enumeration type, like:

# The name “TaxCredit” is also written in CamelCase.
declaration enumeration TaxCredit:
  # The line below says that “TaxCredit” can be a “NoTaxCredit” situation.
  -- NoTaxCredit
  # The line below says that alternatively, “TaxCredit” can be a
  # “ChildrenTaxCredit” situation. This situation carries a content
  # of type integer corresponding to the number of children concerned
  # by the tax credit. This means that if you’re in the “ChildrenTaxCredit”
  # situation, you will also have access to this number of children.
  -- ChildrenTaxCredit content integer

In computer science terms, such an enumeration is called a “sum type” or simply an enum. The combination of structures and enumerations allow the Catala programmer to declare all possible shapes of data, as they are equivalent to the powerful notion of “algebraic data types”.

Notice that these data structures that we have declared cannot always be attached naturally to a particular piece of the specification text. So, where to put these declarations in your literate programming file? Since you will be often going back to these data structure declarations during programming, we advise you to group them together in some sort of prelude in your code source file.

Scopes as basic computation blocks

We’ve defined and typed the data that the program will manipulate. Now we have to define the logical context in which this data will evolve. Because Catala is a functional programming language, all code exists within a function. And the equivalent to a function in Catala is called a scope. Every scope has a name, input variables (similar to function arguments), internal variables (similar to local variables), and output variables (that together form the return type of the function). For instance, Article 1 defines a scope for computing the income tax:

declaration scope IncomeTaxComputation:
  # Scope names use CamelCase.
  input individual content Individual
  # This line declares a scope variable of the scope, which is akin to
  # a function parameter in computer science term. This is the piece of
  # data on which the scope will operate.
  internal fixed_percentage content decimal
  output income_tax content money

The scope is the basic abstraction unit in Catala programs, and scopes can be composed. Since a function can call other functions, scopes can also call other scopes. We will see later how to do this, but first let us focus on the inputs and outputs of scopes.

The declaration of the scope is akin to a function signature: it contains a list of all the arguments along with their types. But in Catala, scopes’ variables can be input, local or output. input means that the variable is provided whenever the scope is called, and cannot be defined within the scope. internal means that the variable is defined within the scope and cannot be seen from outside the scope; it’s not part of the return value of the scope. output means that a caller can retrieve the computed value of the variable. Note that a variable can also be simultaneously an input and an output of the scope, in that case it should be annotated with input output.

Once the scope has been declared, we can use it to define our computation rules and finally code up Article 1!

Defining variables and formulas

Article 1 actually gives the formula to define the income_tax variable of scope IncomeTaxComputation.

Article 1

The income tax for an individual is defined as a fixed percentage of the individual’s income over a year.

scope IncomeTaxComputation:
  definition income_tax equals
    individual.income * fixed_percentage

Let us unpack the code above. Each definition of a variable (here, income_tax) is attached to a scope that declares it (here, IncomeTaxComputation). After equals, we have the actual expression for the variable : individual.income * fixed_percentage. The syntax for formulas uses the classic arithmetic operators. Here, * means multiplying an amount of money by a decimal, returning a new amount of money. The exact behavior of each operator depends on the types of values it is applied on. For instance, here, because a value of the money type is always an integer number of cents, * rounds the result of the multiplication to the nearest cent to provide the final value of type money (see the FAQ for more information about rounding in Catala). About individual.income, we see that the . notation lets us access the income field of individual, which is actually a structure of type Individual.

However, at this point we’re still missing the definition of fixed_percentage. This is a common pattern when coding the law: the definitions for various variables are scattered in different articles. Fortunately, the Catala compiler automatically collects all the definitions for each scope and puts them in the right order. Here, even if we define fixed_percentage after income_tax in our source code, the Catala compiler will switch the order of the definitions internally because fixed_percentage is used in the definition of income_tax. More generally, the order of toplevel definitions and declarations in Catala source code files does not matter, and you can refactor code around freely without having to care about dependency order.

In this tutorial, we’ll suppose that our fictional CTTC specification defines the percentage in the next article. The Catala code below should not surprise you at this point.

Article 2

The fixed percentage mentioned at article 1 is equal to 20 %.

scope IncomeTaxComputation:
  # Writing 20% is just an alternative for the decimal “0.20”.
  definition fixed_percentage equals 20 %

Conditional definitions and exceptions

So far so good, but specifications coming from legal text do not always neatly combine articles dans variable definitions. Sometimes, and this is a very common pattern, a later article redefines a variable already defined previously, but with a twist in a certain exceptional situation. For instance, Article 3 of CTTC:

Article 3

If the individual is in charge of 2 or more children, then the fixed percentage mentioned at article 1 is equal to 15 %.

This article actually gives another definition for the fixed percentage, which was already defined in article 2. However, article 3 defines the percentage conditionally to the individual having more than 2 children. How to redefine fixed_percentage? Catala allows you precisely to redefine a variable under a condition with the under condition ... consequence syntax:

scope IncomeTaxComputation:
  definition fixed_percentage under condition
    individual.number_of_children >= 2
  consequence equals 15 %

What does this mean? If the individual has more than two children, then fixed_percentage will be 15 %. Conditional definitions let you define your variables piecewise, one case at a time; the Catala compiler stitches everything together for execution. More precisely, at runtime, we look at the conditions of all piecewise definitions for a same variable, and pick the one that is valid.

But what happens if no conditional definition is valid at runtime? Or multiple valid definitions at the same time? In these cases, Catala will abort execution and return an error message like the one below:

┌─[ERROR]─
│
│  During evaluation: conflict between multiple valid consequences for assigning the same variable.
│
├─➤ tutorial_en.catala_en
│     │
│     │   definition fixed_percentage equals 20 %
│     │                                      ‾‾‾‾
├─ Article 2
│
├─➤ tutorial_en.catala_en
│     │
│     │   consequence equals 15 %
│     │                      ‾‾‾‾
└─ Article 3

If the specification is correctly drafted, then these error situations should not happen, as one and only one conditional definition should be valid at all times. Here, however, our definition of fixed_percentage conflicts with the more general definition that we gave above. To correctly model situations like this, Catala allows us to define precedence of one conditional definitions over another. It is as simple as adding exception before the definition. For instance, here is a more correct version of the code for Article3 :

Article 3

If the individual is in charge of 2 or more children, then the fixed percentage mentioned at article 1 is equal to 15 %.

scope IncomeTaxComputation:
  exception definition fixed_percentage under condition
    individual.number_of_children >= 2
  consequence equals 15 %

With exception, the conditional definition at Article 3 will be picked over the base case at Article 1 when the individual has two children or more. This exception mechanism is modeled on the logic of legal drafting: it is the key mechanism that lets us split our variables definition to match the structure of the specification. Without exception, it is not possible to use the literate programming style. This is precisely why writing and maintaining computer programs for taxes or social benefits is very difficult with mainstream programming languages. So, go ahead and use exception as much as possible, since it is a very idiomatic Catala concept.

Composing scopes and functions together

Catala is a functional language and encourages using functions to describe relationships between data. As part of our ongoing CTTC specification, we will now imagine an alternative tax system with two progressive brackets. This new example will illustrate how to write more complex Catala programs by composing abstractions together.

First, let us start with the data structure and new scope for our new two-brackets tax computation.

# This structure describes the parameters of a tax computation formula that
# has two tax brackets, each with their own tax rate.
declaration structure TwoBrackets:
  data breakpoint content money
  data rate1 content decimal
  data rate2 content decimal

declaration scope TwoBracketsTaxComputation:
  # This input variable contains the description of the
  # parameters of the tax formula.
  input brackets content TwoBrackets
  # But for declaring the tax_formula variable, we declare it as
  # a function: “content money depends on income content money” means a function
  # that returns money as output (the tax) and takes the “income” money
  # parameter as input.
  output tax_formula content money depends on income content money

The scope TwoBracketsTaxComputation is a generic scope that takes as input the parameters of a two-brackets tax computation, and returns a function that effectively computes the amount of tax under this given two-brackets system. Passing around functions as values is a powerful tool to gain in expressivity when programming, and reduce code duplication and boilerplate to instantiate the same concept multiple times. More importantly, functions as values allow us to sometimes stick closer to the text of the specification that might be very general. Imagine the following Article 4 of the CTTC:

Article 4

The tax amount for a two-brackets computation is equal to the amount of income in each bracket multiplied by the rate of each bracket.

scope TwoBracketsTaxComputation :
  # This is the formula for implementing a two-brackets tax system.
  definition tax_formula of income equals
    if income <= brackets.breakpoint then
      income * brackets.rate1
    else (
      brackets.breakpoint * brackets.rate1 +
      (income - brackets.breakpoint) * brackets.rate2
    )

The above formula for the two-brackets tax system computation also introduces the if ... then ... else ... syntax, that is still available in Catala even if the language encourages the use of conditional definitions and exceptions. Here, we could have defined tax_formula with two conditional definitions, one for income <= brackets.breakpoint, and one for income > brackets.breakpoint. However, the legal specification here does not split the definition of tax_formula in two different article, so it does not make sense to split the definition of tax_formula in the code.

More generally, a rule of thumb for deciding when to split or not variable definitions into conditional definitions is to simply follow what the specification text does. If the specification text splits it definition in multiple paragraphs or sentence, then you can annotate each paragraph or sentence with the corresponding conditional definition. But if the specification introduces the definition in a single block of text, then no need to split the code. For instance, it is more compact to translate a table of values in a specification with if ... then ... else ... statements than conditional definitions, so it may be better to proceed that way.

Now that we’ve defined our helper new scope for computing a two-brackets tax, we want to use it in our main tax computation scope. As mentioned before, Catala’s scope can also be thought of as functions. And sometimes, the specification does implicity translates into a function call, like the article below.

Article 5

For individuals in charge of zero children, the income tax of Article 1 is defined as a two-brackets computation with rates 20% and 40%, with an income breakpoint of $100,000.

To translate Article 5 into Catala code, we need the scope IncomeTaxComputation to call the scope TwoBracketsTaxComputation. One way to write that is to declare TwoBracketsTaxComputation as a static sub-scope of IncomeTaxComputation. This is done by updating the declaration of IncomeTaxComputation and adding a line for the TwoBracketsTaxComputation sub-scope:

declaration scope IncomeTaxComputation:
  # This line says that we add the “two_brackets” as a scope variable.
  # However, the “scope” keyword tells that this item is not a piece of data
  # but rather a subscope that we can use to compute things.
  two_brackets scope TwoBracketsTaxComputation
  input individual content Individual
  output income_tax content money

two_brackets is thus the name of the sub-scope call and we can provide its arguments to code up the two-brackets computation parameters set by Article 5:

scope IncomeTaxComputation :
  # Since the subscope “two_brackets” is like a function we can call,
  # we need to define its arguments. This is done below with the only 
  # parameter “brackets” of sub-scope call “two_brackets” :
  definition two_brackets.brackets equals TwoBrackets {
    -- breakpoint: $100,000
    -- rate1: 20%
    -- rate2: 40%
  }

The sub-scope call two_brackets now has data flowing in to TwoBracketsTaxComputation, letting it compute its output tax_formula, which is the function that we will use to compute the income tax in the case of Article 5, that is when the individual has no children. As for Article 3, we will use an exceptional conditional definition for income_tax, that makes use of two_brackets.tax_formula:

scope IncomeTaxComputation:
  # The syntax of calling a function “f” with argument “x” is “f of x”.
  exception definition income_tax under condition 
    individual.number_of_children = 0
  consequence equals two_brackets.tax_formula of individual.income

The snippet of code below exceptionally calls the function two_brackets.tax_formula when the individual has no children; but two_brackets.tax_formula is itself the output of the scope TwoBracketsTaxComputation called as a sub-scope within IncomeTaxComputation. This pattern of scopes returning functions adheres to the spirit of functional programming, where functions are passed around as values. We encourage you to use this pattern for encoding complex specifications, as it is quite expressive, and does not make use of shared mutable state in memory (which does not exist in Catala anyway).

Complex exceptions patterns

With our last code snippet, note that we introduced our third conditional definition for income_tax: there is one base case, and two exceptions (one if there is more than two children, another if there is zero children). So far, the two exceptions have been simply declared with the exception keyword. That keyword alone suffices because there is only one base case that the exception is refering to. However, sometimes the specification implicitly sets up more complex exception patterns:

Article 6

Individuals earning less than $10,000 are exempted of the income tax mentioned at article 1.

At a first glance, this Article 6 merely defines another exceptional conditional definition for variable income_tax of scope IncomeTaxComputation. But this third exception is likely to conflict with the first one when the individual earns less than $10,000, and has zero children! If such a conflict between exceptions were to happen, the Catala program would crash with an error message similar to the one we already saw when programming Article 3:

┌─[ERROR]─
│
│  During evaluation: conflict between multiple valid consequences for assigning the same variable.
│
├─➤ tutorial_en.catala_en
│     │
│     │   consequence equals two_brackets.tax_formula of individual.income
│     │                      ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
├─ Article 5
│
├─➤ tutorial_en.catala_en
│     │
│     │   consequence equals $0
│     │                      ‾‾
└─ Article 6

In this situation, we need to prioritize the exceptions. This prioritization requires legal expertise and research, as it is not always obvious which exception should prevail in any given situation. Hence, Catala error messages indicating a conflict during evaluation are an invitation to call the lawyer in your team and have them interpret the specification, rather than fixing the conflict yourself.

Here, because Article 6 follows Article 5, and because it is more favorable to the taxpayer to pay $0 in tax rather than the result of the two-brackets computation, we can make the legal decision to prioritize the exception of Article 6 over the exception of Article 5. Now, let us see how to write that with Catala. Because Article 1 is the base case for the exception of Article 5, and Article 5 is the base case for the exception of Article 6, we need to give the definitions of income_tax at Articles 1 and 5 labels so that the exception keywords in Article 5 and 6 can refer to those labels:

Article 1

The income tax for an individual is defined as a fixed percentage of the individual’s income over a year.

scope IncomeTaxComputation:
  label article_1 definition income_tax equals
    individual.income * fixed_percentage

Article 5

For individuals in charge of zero children, the income tax of Article 1 is defined as a two-brackets computation with rates 20% and 40%, with an income breakpoint of $100,000.

scope IncomeTaxComputation:
  label article_5 exception article_1 
  definition income_tax under condition 
    individual.number_of_children = 0
  consequence equals two_brackets.tax_formula of individual.income

Article 6

Individuals earning less than $10,000 are exempted of the income tax mentioned at article 1.

scope IncomeTaxComputation:
  exception article_5 definition income_tax under condition
    individual.income <= $10,000
  consequence equals $0

At runtime, here is how Catala will determine which of the three definitions to pick for income_tax: first, it will try the most exceptional exception (Article 6), and test whether the income is below $10,000; if not, then it will default to the exception level below (Article 5), and test whether there are no children; if not, it will default to the base case (Article 1).

This scenario defines an “exception chain”, but it can get more complex than that. Actually, Catala lets you define “exception trees” as big as you want, simply by providing label and exception tags that refer to each other for your conditional definitions. This expressive power will help you tame the complexity of legal specifications and keep your Catala code readable and maintainable.

Conclusion and next steps

Setting up a Catala project

FAQ: How to code the law?

Booleans and conditions

Money and rounding

In catala, monetary values are represented as an integer number of cents.

A calculation with the catala money type always result in an amount rounded to the nearest cent. This means, that, when performing intermediate computations on money, rounding must be considered by the programmer at each step. This aims at making review by domain experts easier, since for each intermediate value, they can follow along and perform example computations with a simple desk calculator.

The round of builtin will round to the nearest monetary unit (e.g. euro or dollar).

To round an amount to an arbitrary multiple of a cent, perform a multiplication with that multiple, round the amount and divide it by the same amount.

Example: rounding to the nearest multiple of 10 cents

declaration scope Rounding10Cents:
  output result content money

scope Rounding10Cents:
  definition result equals
    round of ($4.13 * 10.) / 10.
$ clerk run round.catala_en --scope Rounding10Cents
[0/1] <catala> interpret Rounding10Cents ⇐ _build/round.catala_en
┌─[RESULT]─
│ result = $4.10
└─

Rounding up or down

To round up or down, add or subtract half a unit before performing the computation and rounding.

Example

To compute a tax amount that is defined as 0.5% of a gross amount (here $149.26) rounded down to the nearest cent

declaration scope Rounding:
  output tax_amount content money

scope Rounding:
  definition tax_amount equals
    money of
      (
        round of
          (
            ((decimal of $149.26) * 0.5% * 100.0) - 0.5
          )
        / 100.
      )

This is a bit of a mouthful, but can be made more readable by introducing a function.

declaration scope Rounding:
  output tax_amount content money
  internal round_down_cents content money depends on amount_decimal content decimal

scope Rounding:
  definition tax_amount equals
    round_down_cents of (decimal of $149.26 * 0.5%)

  definition round_down_cents of amount_decimal equals
    money of ((amount_decimal * 100.0 - 0.5) / 100.)

Besides being more readable, this has the benefit of making it more obvious that any computation that requires keeping a fractional number of cents should be performed on decimals, and converted to a monetary amount at the end.

Can we use Large Language Models (LLMs) to translate the law into code?

Can every law be formalized?

Usual design patterns

Collaborating with lawyers

The Catala language

The clerk build system

The catala compiler