catala/examples/tutorial/tutorial_fr.catala_fr

388 lines
15 KiB
Plaintext
Raw Normal View History

@@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.
@@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.
@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 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 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 13:41:29 +03:00
@@Définir un impôt sur le revenu fictif@@+
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 13:41:29 +03:00
Commençons notre section métadonnées en déclarant l'information sur le type
personne :
2020-12-22 13:41:29 +03:00
@@Début métadonnées@@
/*
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 13:41:29 +03:00
@@Fin métadonnées@@
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 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 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 13:41:29 +03:00
@@Fin métadonnées@@
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 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 13:41:29 +03:00
@@Début métadonnées@@
/*
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 13:41:29 +03:00
@@Fin métadonnées@@
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.
@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.