2020-12-22 11:38:42 +03:00
|
|
|
@@Tutoriel d'utilisation du langage Catala@@
|
|
|
|
|
|
|
|
Bienvenue dans ce tutoriel, son objectif est de vous accompagner dans les
|
|
|
|
fonctionnalités du langage Catala et de vous apprendre à annoter des textes
|
|
|
|
législatifs avec ce langage. Ce document s'adresse principalement à des développeurs
|
|
|
|
ou des personnes ayant déjà programmé, même si des juristes avec des appétences
|
|
|
|
en informatique devraient pouvoir s'en sortir.
|
|
|
|
|
2020-12-22 11:56:20 +03:00
|
|
|
@@Programmation littéraire@@+
|
|
|
|
|
|
|
|
Pour commencer à écrire un programme Catala, vous devez partir du texte
|
|
|
|
d'une source législative qui va justifier le code que vous écrirez.
|
|
|
|
Concrètement, cela signifie faire un copier-coller du texte de la loi dans
|
|
|
|
un fichier de source Catala et le formatter afin que Catala puisse le comprendre.
|
|
|
|
Les fichiers de source Catala ont l'extension ".catala_en" en version anglaise.
|
|
|
|
Si vous écriviez un programme Catala pour une loi française, vous devrez utiliser
|
|
|
|
l'extension ".catala_fr".
|
|
|
|
|
|
|
|
Vous pouvez écrire n'importe quel texte simple en Catala, cela sera affiché
|
|
|
|
sans modification dans une sortie PDF ou HTML. Vous pouvez découper votre texte
|
|
|
|
en de courtes lignes, cela apparaîtera comme un seul paragraphe dans la sortie.
|
|
|
|
Si vous voulez créer un nouveau paragraphe laisser une ligne vierge dans la source.
|
|
|
|
|
|
|
|
Catala vous permet de déclarer des entêtes de section ou de sous-section,
|
|
|
|
comme c'est le cas ici, acec la répétition deux fois de l'arobase. Vous pouvez
|
|
|
|
diminuer l'importance du titre en augmentant le nombre de "+" après le titre de
|
|
|
|
l'entête.
|
|
|
|
|
|
|
|
L'unité de division fondamentale est l'article, commencé par un simple
|
|
|
|
arobase.
|
|
|
|
Étudions un exemple ficitif qui définit un impôt sur le revenu.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
|
|
|
@Article 1@
|
2020-12-22 12:20:06 +03:00
|
|
|
L'impôt sur le revenu d'un individu est calculé en tant qu'un pourcentage
|
|
|
|
fixe des revenus d'une personne pour une année.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
|
|
|
/*
|
2020-12-22 12:20:06 +03:00
|
|
|
# Bienvenue au code Catala. Ceci est un commentaire car la ligne débute par
|
|
|
|
# le caractère #.
|
|
|
|
# Nous allons bientôt apprendre ce que l'on doit écrire ici pour traduire
|
|
|
|
# le sens d'un article de loi en code Catala.
|
2020-12-22 11:38:42 +03:00
|
|
|
*/
|
|
|
|
|
2020-12-22 12:20:06 +03:00
|
|
|
Afin de faire cela, nous allons mélanger de courts bouts de code entre des phrases
|
|
|
|
d'un texte législatif. Chaque bout de code devra être aussi court que possible
|
|
|
|
et aussi proche que possible de la phrase qui justifie le code. Ce style
|
|
|
|
s'appelle programmation littéraire, un paradigme de programmation inventé par le
|
|
|
|
célèbre informaticien Donald Knuth dans les années 70.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Définir un impôt sur le revenu fictif@@+
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Le contenu de l'article 1 utilise beaucoup d'éléments du contexte implicite:
|
|
|
|
il existe une personne avec un revenu et en même temps un impôt sur le revenu,
|
|
|
|
qu'une personne doit acquitter chaque année. Même si ce contexte implicite n'est
|
|
|
|
pas inscrit en tant que tel dans la loi, nous devons l'expliciter pour le traduire
|
|
|
|
en code. Concrètement, nous avons besoin d'une section "métadonnées" qui définit
|
|
|
|
la forme et les types de données contenues dans la loi.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Commençons notre section métadonnées en déclarant l'information sur le type
|
|
|
|
personne :
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Début métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
/*
|
2020-12-22 13:41:29 +03:00
|
|
|
déclaration structure Personne:
|
|
|
|
# Le nom de la structure "Personne", doit commencer
|
|
|
|
# par une lettre majuscule: c'est la convention CamlCase.
|
|
|
|
donnée revenu contenu argent
|
|
|
|
# A cette ligne, revenu est le nom du champ de la structure et
|
|
|
|
# "argent" est le type de de données de ce champ.
|
|
|
|
# On peut utiliser d'autres types comme : entier, décimal, argent, date, durée
|
|
|
|
# ou tout autre structure ou énumération que vous déclarez
|
|
|
|
donnée nombre_enfants contenu entier
|
|
|
|
# "revenu" and "nombre_enfants" commençent par une lettre minuscule,
|
|
|
|
# ils adhèrent à la convention snake_case
|
2020-12-22 11:38:42 +03:00
|
|
|
*/
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Fin métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Cette structure contient deux champs de de données, "revenu" et "nombre_enfants".
|
|
|
|
Les structures sont utiles pour regrouper des données qui vont ensemble.
|
|
|
|
Typiquement, vous aurez une structure pour une entité concrète sur laquelle
|
|
|
|
s'applique la loi (comme une personne). C'est à vous de décider comment regrouper
|
|
|
|
les données mais vous devrez viser à optimiser la lisibilité du code.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Parfois, la loi donne une énumération de différentes situations. Ces énumérations
|
|
|
|
sont modélisés en Catala par le type énumération, comme suit :
|
|
|
|
@@Début métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
/*
|
2020-12-22 13:41:29 +03:00
|
|
|
déclaration énumération CreditImpot:
|
|
|
|
# Le nom "CreditImpot" s'écrit aussi en CamlCase
|
|
|
|
-- AucunCreditImpot
|
|
|
|
# Cette ligne indique que "CreditImpot" peut être en situation "AucunCreditImpot"
|
|
|
|
-- CreditImpotEnfants contenu entier
|
|
|
|
# Cette ligne indique, de manière alternative, que "CreditImpot" peut aussi
|
|
|
|
# être une situation "CreditImpotEnfants". Cette situation porte un contenu
|
|
|
|
# de type entier qui correspond au nombre d'enfants concernés par le crédit
|
|
|
|
# d'impôt. Cela signifie que si vous êtes dans la situation "CreditImpotEnfants",
|
|
|
|
# vous aurez aussi accès à ce nombre d'enfants.
|
2020-12-22 11:38:42 +03:00
|
|
|
*/
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Fin métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
En informatique, une telle énumération est appelée "type somme" ou simplement enum.
|
|
|
|
La combinaison de structures et d'énumération permet au programmeur Catala de déclarer
|
|
|
|
toutes les formes possibles de données, car elles sont équivalentes à la notion puissante
|
|
|
|
de "types de données algébriques".
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Nous avons défini et typé les données que le programme va manipuler. Maintenant,
|
|
|
|
nous devons définir un contexte logique dans lequel ces données vont évoluer.
|
|
|
|
On effectue cela par la notion de "champs d'application" en Catala.
|
|
|
|
Les champs d'application sont proches des fonctions en termes de programmation traditionnelle.
|
|
|
|
Les champs d'application doivent avoir été déclarées préalablement dans les métadonnées, ainsi:
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Début métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
/*
|
2020-12-22 13:41:29 +03:00
|
|
|
déclaration champ d'application CalculImpotRevenu:
|
|
|
|
# Les champs d'application utilisent le CamlCase
|
|
|
|
contexte personne contenu Personne
|
|
|
|
# Cette ligne déclare un élémént de contexte du champ d'application,
|
|
|
|
# cela ressemble à un paramètre de fonction en informatique. C'est la
|
|
|
|
# donnée sur laquelle le champ d'application va intervenir
|
|
|
|
contexte pourcentage_fixe contenu décimal
|
|
|
|
contexte impot_revenu contenu décimal
|
2020-12-22 11:38:42 +03:00
|
|
|
*/
|
2020-12-22 13:41:29 +03:00
|
|
|
@@Fin métadonnées@@
|
2020-12-22 11:38:42 +03:00
|
|
|
|
2020-12-22 13:41:29 +03:00
|
|
|
Nous avons maintenant tout ce dont nous avons besoin pour annoter le contenu
|
|
|
|
de l'article 1 qui a été copié ci-dessous.
|
2020-12-22 11:38:42 +03:00
|
|
|
|
|
|
|
@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
|
|
|
|
*/
|
|
|
|
|
|
|
|
In the code, we are defining inside our scope the amount of the income tax
|
|
|
|
according to the formula described in the article. When defining formulaes,
|
|
|
|
you have access to all the usual arithmetic operators: addition "+",
|
|
|
|
substraction "-", multiplication "*" and division (slash).
|
|
|
|
|
|
|
|
However, in the Catala code, you can see that we use "*$" to multiply the
|
|
|
|
individual income by the fixed percentage. The $ suffix indicates that we
|
|
|
|
are performing a multiplication on an amount of money. Indeed, in Catala,
|
|
|
|
you have to keep track of what you are dealing with: is it money ? Is it
|
|
|
|
an integer? Using just "+" or "*" can be ambiguous in terms of rounding,
|
|
|
|
since money is usually rounded at the cent. So to disambiguate, we suffix these
|
|
|
|
operations with something that indicates the type of what we manipulate.
|
|
|
|
The suffixes are "$" for money "." for decimals, "at" (like in email adresses)
|
|
|
|
for dates and the hat symbol for durations. If you forget the suffix, the Catala type
|
|
|
|
checker will display an error message that will help you put it where it
|
|
|
|
belongs.
|
|
|
|
|
|
|
|
But inside article 1, one question remains unknown: what is the value of
|
|
|
|
of the fixed percentage? Often, precise values are defined elsewhere in the
|
|
|
|
legislative source. Here, let's suppose we have:
|
|
|
|
|
|
|
|
@Article 2@
|
|
|
|
The fixed percentage mentionned at article 1 is equal to 20 %.
|
|
|
|
/*
|
|
|
|
scope IncomeTaxComputation:
|
|
|
|
definition fixed_percentage equals 20 %
|
|
|
|
# Writing 20% is just an abbreviation for 0.20
|
|
|
|
*/
|
|
|
|
|
|
|
|
You can see here that Catala allows definitions to be scattered throughout
|
|
|
|
the annotation of the legislative text, so that each
|
|
|
|
definition is as close as possible to its location in the text.
|
|
|
|
|
|
|
|
@@Conditional definitions@@+
|
|
|
|
|
|
|
|
So far so good, but now the legislative text introduces some trickyness. Let us
|
|
|
|
suppose the third article says:
|
|
|
|
|
|
|
|
@Article 3@ If the individual is in charge of 2 or more children, then the fixed
|
|
|
|
percentage mentionned at article 1 is equal to 15 %.
|
|
|
|
/*
|
|
|
|
# How to redefine fixed_percentage?
|
|
|
|
*/
|
|
|
|
|
|
|
|
This article actually gives another definition for the fixed percentage, which
|
|
|
|
was already defined in article 2. However, article 3 defines the percentage
|
|
|
|
conditionnally to the individual having more than 2 children. Catala allows
|
|
|
|
you precisely to redefine a variable under a condition:
|
|
|
|
|
|
|
|
/*
|
|
|
|
scope IncomeTaxComputation:
|
|
|
|
definition fixed_percentage under condition
|
|
|
|
individual.number_of_children >= 2
|
|
|
|
consequence equals 15 %
|
|
|
|
# Writing 15% is just an abbreviation for 0.15
|
|
|
|
*/
|
|
|
|
|
|
|
|
When the Catala program will execute, the right definition will be dynamically
|
|
|
|
chosen by looking at which condition is true. A correctly drafted legislative
|
|
|
|
source should always ensure that at most one condition is true at all times.
|
|
|
|
However, if it is not the case, Catala will let you define a precedence on the
|
|
|
|
conditions, which has to be justified by the law.
|
|
|
|
|
|
|
|
|
|
|
|
@@Functions@@+
|
|
|
|
|
|
|
|
Catala lets you define functions anywhere in your data. Here's what it looks
|
|
|
|
like in the metadata definition when we want to define a two-brackets tax
|
|
|
|
computation:
|
|
|
|
@@Begin metadata@@
|
|
|
|
/*
|
|
|
|
declaration structure TwoBrackets:
|
|
|
|
data breakpoint content money
|
|
|
|
data rate1 content decimal
|
|
|
|
data rate2 content decimal
|
|
|
|
|
|
|
|
declaration scope TwoBracketsTaxComputation :
|
|
|
|
context brackets content TwoBrackets
|
|
|
|
context tax_formula content money depends on money
|
|
|
|
*/
|
|
|
|
@@End metadata@@
|
|
|
|
|
|
|
|
And in the code:
|
|
|
|
|
|
|
|
@Article4@ 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 :
|
|
|
|
definition tax_formula of income equals
|
|
|
|
if income <=$ brackets.breakpoint then
|
|
|
|
income *$ brackets.rate1
|
|
|
|
else (
|
|
|
|
brackets.breakpoint *$ brackets.rate1 +$
|
|
|
|
(income -$ brackets.breakpoint) *$ brackets.rate2
|
|
|
|
)
|
|
|
|
*/
|
|
|
|
|
|
|
|
@@Scope inclusion@@+
|
|
|
|
|
|
|
|
Now that we've defined our helper scope for computing a two-brackets tax, we
|
|
|
|
want to use it in our main tax computation scope.
|
|
|
|
|
|
|
|
@Article 5@ For individuals whose income is greater than $100,000, the income
|
|
|
|
tax of article 1 is 40% of the income above $100,000. Below $100,000, the
|
|
|
|
income tax is 20% of the income.
|
|
|
|
/*
|
|
|
|
declaration scope NewIncomeTaxComputation:
|
|
|
|
context two_brackets scope TwoBracketsTaxComputation
|
|
|
|
# This line says that we add the item two_brackets_for_rich to the context.
|
|
|
|
# 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.
|
|
|
|
context individual content Individual
|
|
|
|
context income_tax content money
|
|
|
|
|
|
|
|
scope NewIncomeTaxComputation :
|
|
|
|
definition two_brackets.brackets equals TwoBrackets {
|
|
|
|
-- breakpoint: $100,000
|
|
|
|
-- rate1: 20%
|
|
|
|
-- rate2: 40%
|
|
|
|
}
|
|
|
|
definition income_tax equals two_brackets.tax_formula of individual.income
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Article 6@
|
|
|
|
Individuals earning less than $10,000 are exempted of the income tax mentionned
|
|
|
|
at article 1.
|
|
|
|
/*
|
|
|
|
scope NewIncomeTaxComputation:
|
|
|
|
definition income_tax under condition
|
|
|
|
individual.income <=$ $10,000
|
|
|
|
consequence equals $0
|
|
|
|
*/
|
|
|
|
|
|
|
|
That's it! We've defined a two-brackets tax computation simply by annotating
|
|
|
|
legislative article by snippets of Catala code. However, attentive readers
|
|
|
|
may have caught something weird in articles 5 and 6. What happens when the
|
|
|
|
income of the individual is between $10,000 and $100,000 ?
|
|
|
|
|
|
|
|
The law leaves it unspecified ; our dummy articles are clearly badly drafted.
|
|
|
|
But Catala can help you find this sort of errors via simple testing or
|
|
|
|
even formal verification. Let's start with the testing.
|
|
|
|
|
|
|
|
@@Testing Catala programs@@+
|
|
|
|
|
|
|
|
Testing Catala programs can be done directly into Catala. Indeed, writing test
|
|
|
|
cases for each Catala scope that you define is a good practice called
|
|
|
|
"unit testing" in the software engineering community. A test case is defined
|
|
|
|
as another scope:
|
|
|
|
|
|
|
|
@Testing NewIncomeTaxComputation@
|
|
|
|
/*
|
|
|
|
declaration scope Test1:
|
|
|
|
context tax_computation scope NewIncomeTaxComputation
|
|
|
|
|
|
|
|
scope Test1:
|
|
|
|
definition
|
|
|
|
tax_computation.individual
|
|
|
|
# We define the argument to the subscope
|
|
|
|
equals
|
|
|
|
# The four lines below define a whole structure by giving a value to
|
|
|
|
# each of its fields
|
|
|
|
Individual {
|
|
|
|
-- income: $230,000
|
|
|
|
-- number_of_children: 0
|
|
|
|
}
|
|
|
|
|
|
|
|
# Next, we retrieve the income tax value compute it by the subscope and
|
|
|
|
# assert that it is equal to the expected value :
|
|
|
|
# ($230,000-$100,00)*40%+$100,000*20% = $72,000
|
|
|
|
assertion tax_computation.income_tax = $72,000
|
|
|
|
*/
|
|
|
|
|
|
|
|
This test should pass. Let us now consider a failing test case:
|
|
|
|
/*
|
|
|
|
declaration scope Test2:
|
|
|
|
context tax_computation scope NewIncomeTaxComputation
|
|
|
|
|
|
|
|
scope Test2:
|
|
|
|
definition tax_computation.individual equals Individual {
|
|
|
|
-- income: $4,000
|
|
|
|
-- number_of_children: 0
|
|
|
|
}
|
|
|
|
|
|
|
|
assertion tax_computation.income_tax = $0
|
|
|
|
*/
|
|
|
|
|
|
|
|
This test case should compute a $0 income tax because of Article 6. But instead,
|
|
|
|
execution will yield an error saying that there is a conflict between rules.
|
|
|
|
|
|
|
|
@@Defining exceptions to rules@@+
|
|
|
|
|
|
|
|
Indeed, the definition of the income tax in article 6 conflicts with the
|
|
|
|
definition of income tax in article 5. But actually, article 6 is just an
|
|
|
|
exception of article 5. In the law, it is implicit that if article 6 is
|
|
|
|
applicable, then it takes precedence over article 5.
|
|
|
|
|
|
|
|
@Fixing the computation@
|
|
|
|
|
|
|
|
This implicit precedence has to be explicitely declared in Catala. Here is a
|
|
|
|
fixed version of the NewIncomeTaxComputation scope:
|
|
|
|
|
|
|
|
/*
|
|
|
|
declaration scope NewIncomeTaxComputationFixed:
|
|
|
|
context two_brackets scope TwoBracketsTaxComputation
|
|
|
|
context individual content Individual
|
|
|
|
context income_tax content money
|
|
|
|
|
|
|
|
scope NewIncomeTaxComputationFixed :
|
|
|
|
definition two_brackets.brackets equals TwoBrackets {
|
|
|
|
-- breakpoint: $100,000
|
|
|
|
-- rate1: 20%
|
|
|
|
-- rate2: 40%
|
|
|
|
}
|
|
|
|
|
|
|
|
# To define an exception to a rule, you have to first label the rule that
|
|
|
|
# you want to attach to exception to. You can put any snake_case identifier
|
|
|
|
# for the label
|
|
|
|
label article_5
|
|
|
|
definition income_tax equals two_brackets.tax_formula of individual.income
|
|
|
|
|
|
|
|
# Then, you can declare the exception by referring back to the label
|
|
|
|
exception article_5
|
|
|
|
definition income_tax under condition
|
|
|
|
individual.income <=$ $10,000
|
|
|
|
consequence equals $0
|
|
|
|
*/
|
|
|
|
|
|
|
|
And the test that should now work:
|
|
|
|
|
|
|
|
/*
|
|
|
|
declaration scope Test3:
|
|
|
|
context tax_computation scope NewIncomeTaxComputationFixed
|
|
|
|
|
|
|
|
scope Test3:
|
|
|
|
definition tax_computation.individual equals Individual {
|
|
|
|
-- income: $4,000
|
|
|
|
-- number_of_children: 0
|
|
|
|
}
|
|
|
|
assertion tax_computation.income_tax = $0
|
|
|
|
*/
|
|
|
|
|
|
|
|
@@Conclusion@@+
|
|
|
|
|
|
|
|
This tutorial present the basic concepts and syntax of the Catala language
|
|
|
|
features. It is then up to you tu use them to annotate legislative texts
|
|
|
|
with their algorithmic translation.
|
|
|
|
|
|
|
|
There is no single way to write Catala programs, as the program style should be
|
|
|
|
adapted to the legislation it annotates. However, Catala is a functional
|
|
|
|
language at heart, so following standard functional programming design patterns
|
|
|
|
should help achieve concise and readable code.
|