Definitions and exceptions

While the previous reference section covered the declaration of scopes (introduced by declaration scope Foo) that have to be done once for each scope in the codebase, this section covers the definition of scope variables scattered across the literate programming codebase (introduced by scope Foo).

Everything happens inside a scope

Scope variable definitions and assertions only make sense inside a given scope, which is why all the examples below will show the feature inside a scope Foo block that we assume has been already declared elsewhere.

The full syntax of what we will be covering in this section is :

scope <scope_name>:
  [label <label_name>]
  [exception <label_name>]
  definition <scope_variable_name>
    [of <parameters>] [state <state_name>]
    [under condition <expression> consequence]
    equals <expression>

  assertion <expression>

Scope variable definitions

Scope variable definitions is where the bulk of the Catala code will live. Defining a variable bar inside scope Foo as the value 42 is as simple as:

scope Foo:
  definition bar equals 42

Of course, you can swap 42 by any expression as long it has the correct type with respect to the scope variable declaration.

Scope variables that are functions

If the scope variable you are defining is a function variable, for instance if bar is a function of scope Foo with arguments x and y, then the syntax for definition the variable is:

scope Foo:
  definition bar of x,y equals x + y

Scope variables with multiple states

If the scope variable has several states, for instance if bar has states beginning, middle and last, then the syntax for defining the state middle of the variable is:

scope Foo:
  definition bar state middle equals bar * 2
  # "bar" above refers to the value of bar in the previous state,
  # here "beginning"

Variable states and functions mix this way:

scope Foo:
  definition bar of x, y state middle equals (bar of x, y) * 2 + x + y

Scope variables that are condition

The condition scope variables (boolean scope variables with a default value of false) have a different, lawyer-friendly syntax for the definitions: definition is replaced by rule and equals <expression> is replaced by fulfilled or not fulfilled:

scope Foo:
  # Rules usually always come in the form of conditional definitions, see below
  rule bar under condition ... consequence not fulfilled

Conditional definitions

The main feature of Catala is to be able to break down scope variable definitions into multiple pieces. Indeed, legal texts often define things piece-wise in several articles, each article dealing with one specific situation yielding one specific definition for a quantity. This pattern is reflected in Catala under the form of conditional definitions.

You can define a variable multiple times !

As covered in the tutorial, it is totally expected in Catala for a given scope variable to have multiple definitions in the codebase. The code will "work" because those multiple definitions are conditional, and each will only trigger if its condition is met.

Conditions are introduced in scope variable definitions just before the equals keyword with the following syntax :

scope Foo:
  definition bar under condition fizz >= 0 consequence 42

Follow the relevant tutorial section for more information about how to use conditional definitions for encoding legal provisions.

Exceptions and priorities

When there are multiple conditional or non-conditional definitions for a given scope variables, it is sometimes necessary to disambiguate which is going to prevail when the conditions for 2 or more definitions trigger at the same time. This is done by defining a structure of exceptions between the multiple definitions of the same scope variable. This structure of exceptions is actually a tree, because you can have exceptions of exceptions.

Follow the relevant tutorial section for more information about how to use exceptions for encoding legal provisions.

Declaring one exceptional definition to a single base case definition

Let us start with the most basic case. Each node of the exceptions tree for a given scope variable starts with a conditional or non-conditional definitions. You can give a label to this node of the tree with the label syntax:

scope Foo:
  label base_case definition bar equals 42

Then, later in the codebase, if you want to add an exception to this base_case of the definition of bar, you will do so with the syntax:

scope Foo:
  # The line below means that the current definition is an exception *to*
  # the other definition with the "base_case" label.
  exception base_case
  definition bar under condition
    fizz = 0
  consequence equals 0

Do we have to give a label to each definition all the time?

In this setting with only one base case definition and one exceptional definition, there is only one choice as to what the exception is referring to (the other definition). In those cases where it is unambiguous what you are defining an exception to, you can drop the label and simply write:

scope Foo:
  definition bar equals 42

...

scope Foo:
  exception definition bar under condition
    fizz = 0
  consequence equals 0

If there is any ambiguity with your setup using this short-hand format, the Catala compiler will warn you. Anyway, it is always clearer and better practice to give labels to definitions, especially with complex exception structures.

Declaring multiple definitions as one exception to a single definition

In the previous example, bar was set exceptionnally to 0 if fizz = 0. What if we want to expand that behavior with two more exceptional definitions that set bar to 1 and -1 respectively when fizz > 0 and fizz < 0? These three exceptional definitions cannot conflict with each other because fizz is either positive, negative or 0. So, these three exceptional definitions behave like one big exception to the base case (where bar is set to 42).

To group the three exceptional definitions together in Catala and set them as an exception to the base case, it suffices to give the three exceptional definitions the same label fizz_exn (you can choose the label name) and exception indication:

scope Foo:
  label base_case definition bar equals 42

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz = 0
  consequence equals 0

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz > 0
  consequence equals 1

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz < 0
  consequence equals -1

Could we drop some labels here?

The two labels base_case and fizz_exn above make very clear what is happening, at the expense of being a bit verbose. In this example, since there is only one definition in the base case, it is unambiguous to what the exceptional definitions are exception to, and if they're all exceptions to the same thing without any labels, they will be grouped implicitly together. So, you could have dropped all the labels are write:

scope Foo:
  definition bar equals 42

...

scope Foo:
  exception definition bar under condition
    fizz = 0
  consequence equals 0

...

scope Foo:
  exception definition bar under condition
    fizz > 0
  consequence equals 1

...

scope Foo:
  exception definition bar under condition
    fizz < 0
  consequence equals -1

Declaring multiple definitions as one exception to a group of definitions

To build on our running example, imagine now that the value of bar in the base case drifts over time: 42 before 2025 but 43 after. Then, you need two conditional definitions in the base case (those two grouped definitions are still mutually exclusive). This is achieved in Catala simply by giving the samel label base_case to the two base case definitions:

scope Foo:
  label base_case definition bar under condition
    current_date < |2025-01-01|
  equals 42

...

scope Foo:
  label base_case definition bar under condition
    current_date >= |2025-01-01|
  equals 43

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz = 0
  consequence equals 0

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz > 0
  consequence equals 1

...

scope Foo:
  label fizz_exn
  exception base_case
  definition bar under condition
    fizz < 0
  consequence equals -1

Could we drop some labels here?

No here the situation is already complex enough to create ambiguity as to what the exceptional rules are an exception of.

Stacking exceptions, and more

The previous examples show how to use the label and exception syntax to group definitions together and declare exceptional priorities. This syntax is all you need! In particular, you can stack exceptions by defining chains and branches of exception relationship, etc. This is covered extensively in the relevant section of the tutorial.

Assertions

Assertions in Catala are expressions attached to scopes (they can depend on scope variables) that should always be true. They can be used for:

  • input sanitization and scope preconditions;
  • checking logical invariants;
  • testing an expected output.

Their syntax is as simple as:

scope Foo:
  assertion bar + 2 = fizz * 4

Date rounding mode

To set the date computation rounding mode to either up or down (see the relevant reference section), for all the date operations inside a whole scope, use this syntax:

# Let us suppose you want to set the rounding more for date operations
# inside scope Foo declared elsewhere
scope Foo:
  date round decreasing # rounding down
  # or
  date round increasing # rounding up