Conditional definitions and exceptions

In this section, the tutorial introduces the killer feature of Catala when it comes to coding the law: exceptions in the definitions of variables. By the end of the section, you should understand the behavior of computations involving exceptions, and be able to structure groups of variable definitions according to their exceptional status and relative priority.

Recap of the previous section

This section of the tutorial builds up on the previous one, and will reuse the same running example, but all the Catala code necessary to execute the example is included below for reference.

```catala
declaration structure Individual:
  data income content money
  data number_of_children content integer

declaration scope IncomeTaxComputation:
  input individual content Individual
  internal tax_rate content decimal
  output income_tax content money
```

## Article 1

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

```catala
scope IncomeTaxComputation:
  definition income_tax equals
    individual.income * tax_rate
```

## Article 2

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

```catala
scope IncomeTaxComputation:
  label article_2
  definition tax_rate equals 20%
```

## Test

```catala
declaration scope Test:
  output computation content IncomeTaxComputation

scope Test:
  definition computation equals
    output of IncomeTaxComputation with {
      -- individual:
        Individual {
          -- income: $20,000
          -- number_of_children: 0
        }
    }
```

Conditional definitions and exceptions

Specifications coming from legal text do not always neatly divide up each variable definition into its own article. 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 tax_rate? Catala allows you precisely to redefine a variable under a condition with the under condition ... consequence syntax between the name of the variable being defined and the equals keyword:

Defining a variable conditionally

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

What does this mean? If the individual has more than two children, then tax_rate 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.

Conflict between definitions

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 tax_rate 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 tax_rate 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 article 3:

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 %.

Defining an exception for a variable

scope IncomeTaxComputation:
  exception definition tax_rate 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.

How are exceptions computed?

When defining exceptions in your Catala code, it is importand to understand precisely their underlying semantics, i.e. what will be the end result of the computation. The semantics of Catala are formally defined and based on prioritized default logic, which translates intuitively to the following algorithm describing how to compute exceptions:

  1. Gather all definitions (conditional or not) for a given variable;
  2. Among these definitions, check all that apply (whose conditions evaluate to true):
    • If no definition apply, the program crashes with an error ("no definitions apply");
    • If only one definition applies, then pick it and continue the execution of the program;
    • If multiple definitions apply, then check their priorities:
      • If there exists one definition that is an exception to all the others that apply, pick it and continue the execution of the program;
      • Otherwise, the program crashes with an error ("conflicting definitions").

As described above, putting exception in a Catala program alters the behavior of the program, by providing a priority between conditional definitions of a variable that Catala can use at execution time when hesitating between multiple definitions that apply at the same time. So far, we have seen a very simple situation with one base definition (in article 2) and a single exception (in article 3). But the exception mechanism can be much broader and help set different priority lines among dozens of different conditional definitions for a same variable. Let us explore this mechanism on a more complex example.

Dealing with multiple exceptions

It is frequent in legal text that an article setting up a general rule is followed by multiple articles defining exceptions to the base rule. Additionally to article 3 and the reduced tax rate for large families, the CTTC (Catala Tutorial Tax Code) indeed defines a tax exemption for low-income individuals, that we can encode as another exception to the definition of the tax rate in article 2:

Article 4

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

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

But then, what happens when testing the code with an individual that earns less than $10,000 and has more than 2 children?

Conflicting definitions

The program execution yields the following error at runtime:

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

In this situation, both conditional definitions from article 3 and article 4 apply, but also the base definition from article 2. We know article 3 and article 4 are exceptions to article 2, hence they both have priority over it. But we don't know which definition has priority between article 3 and article 4, hence the error message above!

In this situation, we need to prioritize the exceptions between each other.

Here, because article 4 follows article 3, and because it is more favorable to the taxpayer to pay $0 in tax rather than 15 % of their income, we can make the legal decision to prioritize the exception of article 4 over the exception of article 3. Now, let us see how to write that with Catala. Because article 2 is the base case for the exception of article 3, and article 3 is the base case for the exception of article 4, we need to give the definitions of tax_rate at articles 2 and 3 an explicit label so that the exception keywords in article 3 and 4 can refer to those labels:

Pointing exceptions to specific labels

Article 2

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

scope IncomeTaxComputation:
  # The keyword "label" introduces the name of the label itself, here
  # "article_2".
  label article_2 definition tax_rate equals 20 %

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:
  # This definition is preceded by two indications:
  # * it has its own label, "article_3";
  # * this definition is an exception to the definition labeled "article_2".
  label article_3 exception article_2
  definition tax_rate under condition
    individual.number_of_children >= 2
  consequence equals 15 %

Article 4

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

scope IncomeTaxComputation:
  label article_4 exception article_3
  definition tax_rate under condition
    individual.income <= $10,000
  consequence equals 0 %

Thanks to labels, we can define chains of exceptions, where each definition is the exception to the previous one, and the base case for the next one. This pattern is the most usual in legal texts, and its behavior is straightfoward: when multiple definitions apply, pick the one with the highest priority in the chain. Here's a representation of the exception chain in our example so far:

graph LR;
A2["`article_2`"]
A3["`article_3`"]
A4["`article_4`"]
A2-->A3
A3-->A4

But sometimes, it's not possible to arrange exceptions in a chain, since legal interpretation leads to different branches of exceptions.

Branches of exceptions

It may be difficult to see why some legal situations may lead to different branches of exceptions. Let us provide an example with a new article of the CTTC:

Article 5

Individuals earning more than $100,000 are subjects to a tax rate of 30%, regardless of their number of children.

scope IncomeTaxComputation:
  label article_5 exception article_3
  definition tax_rate under condition
    individual.income > $100,000
  consequence equals 30 %

Now, article 3 has two exceptions : article 4, and article 5. These two exceptions have the following conditions:

  • Article 4: income less than $10,000 ;
  • Article 5: income more than $100,000.

Displaying the exception branches

As the codebase grows, it becomes more and more difficult to visualized all the conditional definitions of a variable as well as the prioritization between them. To help, the Catala compiler can display the exception branches with the following command:

$ catala exceptions tutorial.catala_en --scope=IncomeTaxComputation --variable=tax_rate
┌[RESULT]─
│ Printing the tree of exceptions for the definitions of variable "tax_rate" of scope "IncomeTaxComputation".
└─
┌─[RESULT]─
│ Definitions with label "article_2":
│
├─➤ tutorial.catala_en
│    │
│    │   label article_2 definition tax_rate equals 20 %
│    │   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
└─ Title
   └─ Article 2
┌─[RESULT]─
│ Definitions with label "article_3":
│
├─➤ tutorial.catala_en
│    │
│    │   label article_3 exception article_2 definition tax_rate under condition
│    │   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
└─ Title
   └─ Article 3
┌─[RESULT]─
│ Definitions with label "article_4":
│
├─➤ tutorial.catala_en
│    │
│    │   label article_4 exception article_3 definition tax_rate under condition
│    │   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
└─ Title
   └─ Article 4
┌─[RESULT]─
│ Definitions with label "article_5":
│
├─➤ tutorial.catala_en
│    │
│    │   label article_5 exception article_3 definition tax_rate under condition
│    │   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
└─ Title
   └─ Article 5
┌─[RESULT]─
│ The exception tree structure is as follows:
│
│ "article_2"───"article_3"──┬──"article_5"
│                            │
│                            └──"article_4"
└─

Theoretically, since the exceptions of article 4 and article 5 are not prioritized with each other, they could both apply at the same time and conflict. However, since the income cannot be both less than $10,000 and greater than $100,000, the conflict cannot happen in practice. Hence, it is not necessary to prioritize the two exceptions, since they live in mutually exclusive conditional branches. It is then possible to extend these branches separately, for instance with a new article of the CTTC:

Article 6

In the overseas territories, the tax rate for individuals earning more than $100,000 specified at article 5 is reduced to 25 %.

This article introduces a new bit of information about the tax computation: are we in an overseas territory or not? We can model it with a new input to the scope IncomeTaxComputation, leading to a revised scope declaration:

Revised scope declaration

declaration scope IncomeTaxComputation:
   input individual content Individual
   input overseas_territories content boolean
   internal tax_rate content decimal
   output income_tax content money

With this new input variable, the code for article 6 is as follows:

scope IncomeTaxComputation:
  label article_6 exception article_5
  definition tax_rate under condition
    individual.income > $100,000 and overseas_territories
  consequence equals 25 %

Exceptions do not inherit conditionals from their base case

Note that in the condition for defining tax_rate in article 6, we have repeated the condition individual.income > $100,000 in conjunction with the new clause overseas_territories. In our fictional CTTC, the text of article 6 is gently worded and explicitly reminds us that this exception to article 5 only applies in the situations that also trigger article 5 (where the income is greater than $100,000).

However, the legal text can sometimes omit this key information, or make it implicit, creating a danger of putting the wrong conditional in the Catala code in presence of exception branches. Suppose we had omitted the income condition in the code for article 6:

scope IncomeTaxComputation:
  label article_6 exception article_5
  definition tax_rate under condition
    overseas_territories
  consequence equals 25 %

With this code, the article 6 definition would have applied in overseas territories for all individuals, including those earning less than $100,000! Indeed, exceptional definitions in Catala do not inherit the conditions of their base case: the condition of article 6 does not inherit the condition of article 5, we need to repeat it in article 6 if we want to have the correct activation pattern.

Finally, we can recap the collection of exception branches as a tree of exceptions for our example:

graph LR;
A2["`article_2`"]
A3["`article_3`"]
A4["`article_4`"]
A5["`article_5`"]
A6["`article_6`"]
A2-->A3
A3-->A4
A3-->A5
A5-->A6

Grouping conditional definitions together for exceptions

So far, we have seen how to define exception chains and mutually exclusive exception branches. But there is a very common pattern that introduces yet another exceptional shenanigan. Suppose than in the year 2000, a big tax reform changes the base taxation rate of article 2 with a slight increase:

Article 2 (new version after 2000)

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

Now, there are several strategies to deal with legal updates in Catala, that are summed up in the how to section of this book. But here, we'll suppose that we want both versions of the law (before and after 2000) to coexist in the same Catala program. This choice leads us to introduce the current date as a new input of the scope IncomeTaxComputation:

Revised scope declaration

declaration scope IncomeTaxComputation:
   input current_date content date
   input individual content Individual
   input overseas_territories content boolean
   internal tax_rate content decimal
   output income_tax content money

This current_date variable will allow us to introduce mutually exclusive conditional definitions for the two different verions of article 2, each one activating only before of after the year 2000. Note that is the two definitions of article 2 were not mutually exclusive, they could conflict with each other, forcing you to prioritize between them and change the shape of the overall exception tree by introducing another layer of exception. However, we want in this cas those two base definitions of article 2 to collectively be the base case for all subsequent exceptions in the exception tree of tax_rate! In a nutshell, we want the following exception tree:

graph LR;
subgraph article_2
direction TB
A2["`article_2 (before 2000)`"]
A2bis["`article_2 (after 2000)`"]
end
A3["`article_3`"]
A4["`article_4`"]
A5["`article_5`"]
A6["`article_6`"]
A2~~~A2bis
article_2-->A3
A3-->A4
A3-->A5
A5-->A6

Catala is able to represent this exception tree, by grouping together the two conditional definitions to article 2. Indeed, since article 3 is an exception to the label article_2, it suffices to give the same label article_2 to the two conditional definitions of the two versions of article_2:

Grouping mutually exclusive conditional definitions

Article 2 (old version before 2000)

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

scope IncomeTaxComputation:
  label article_2 definition tax_rate under condition
    current_date < |2000-01-01|
  consequence equals 20 %

Article 2 (new version after 2000)

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

scope IncomeTaxComputation:
  # Simply use the same label "article_2" as the previous definition to group
  # them together
  label article_2 definition tax_rate under condition
    current_date >= |2000-01-01|
  consequence equals 21 %

By using the definition grouping mechanism along with exception branches, Catala is able to express a wide range of legal text logic, and helps keeping the code alongside its specification.

Checkpoint

This concludes the second section of the tutorial. In Catala, variables can be defined piece-wise, each piece of definition being activated by a condition. When multiple conditions apply, one can prioritize the conditional definitions using the exception and label keywords to form exception trees able to capture the complex logic behind the legal texts while matching their structure.

Recap of the current section

For reference, here is the final version of the Catala code consolidated at the end of this section of the tutorial.

```catala
declaration structure Individual:
  data income content money
  data number_of_children content integer

declaration scope IncomeTaxComputation:
  input current_date content date
  input individual content Individual
  input overseas_territories content boolean
  internal tax_rate content decimal
  output income_tax content money
```

## Article 1

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

```catala
scope IncomeTaxComputation:
  definition income_tax equals
    individual.income * tax_rate
```

## Article 2 (old version before 2000)

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

```catala
scope IncomeTaxComputation:
  label article_2
  definition tax_rate under condition
    current_date < |2000-01-01|
  consequence equals 20%
```

## Article 2 (new version after 2000)

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

```catala
scope IncomeTaxComputation:
  # Simply use the same label "article_2" as the previous definition to group
  # them together
  label article_2
  definition tax_rate under condition
    current_date >= |2000-01-01|
  consequence equals 21%
```

## 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 %.

```catala
scope IncomeTaxComputation:
  label article_3 exception article_2
  definition tax_rate under condition
    individual.number_of_children >= 2
  consequence equals 15%
```

## Article 4

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

```catala
scope IncomeTaxComputation:
  label article_4 exception article_3
  definition tax_rate under condition
    individual.income <= $10,000
  consequence equals 0%
```

## Article 5

Individuals earning more than $100,000 are subjects to a tax rate of
30%, regardless of their number of children.

```catala
scope IncomeTaxComputation:
  label article_5 exception article_3
  definition tax_rate under condition
    individual.income > $100,000
  consequence equals 30%
```

## Article 6

In the overseas territories, the tax rate for individuals earning
more than $100,000 specified at article 5 is reduced to 25 %.

```catala
scope IncomeTaxComputation:
  label article_6 exception article_5
  definition tax_rate under condition
    individual.income > $100,000 and overseas_territories
  consequence equals 25%
```

## Test

```catala
declaration scope Test:
  output computation content IncomeTaxComputation

scope Test:
  definition computation equals
    output of IncomeTaxComputation with {
      -- individual:
        Individual {
          -- income: $20,000
          -- number_of_children: 0
        }
      -- overseas_territories: false
      -- current_date: |1999-01-01|
    }
```