diff --git a/catala-setup.md b/catala-setup.md deleted file mode 100644 index 9f33bd3..0000000 --- a/catala-setup.md +++ /dev/null @@ -1,133 +0,0 @@ -# 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](https://opam.ocaml.org/doc/Install.html) (version >= 2.2) - -With aptitude (debian-like linux distributions): -```bash -$ sudo apt update -$ sudo apt install opam -``` -Without aptitude: -```bash -$ bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)" -``` - -Then, initialize `opam`: -```bash -$ opam init --bare -``` - -### Getting Catala - -Run the following command to install the latest Catala version via `opam`: - -```bash -$ 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: - -```bash -$ opam pin add catala git+https://github.com/CatalaLang/catala-lsp -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: -```bash -$ 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](https://opam.ocaml.org/doc/Install.html) by invoking -```powershell -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`: -```bash -$ 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: - -```bash -$ 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. diff --git a/src/1-1-installing.md b/src/1-1-installing.md index 9d2e925..8a4c447 100644 --- a/src/1-1-installing.md +++ b/src/1-1-installing.md @@ -1,5 +1,135 @@ # Installing Catala on your machine -## Installing the Catala compiler and LSP server +# Catala installation instructions -## Setting up the Catala IDE in VSCode +**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](https://opam.ocaml.org/doc/Install.html) (version >= 2.2) + +With aptitude (debian-like linux distributions): +```bash +$ sudo apt update +$ sudo apt install opam +``` +Without aptitude: +```bash +$ bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)" +``` + +Then, initialize `opam`: +```bash +$ opam init --bare +``` + +### Getting Catala + +Run the following command to install the latest Catala version via `opam`: + +```bash +$ 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: + +```bash +$ opam pin add catala git+https://github.com/CatalaLang/catala-lsp -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: +```bash +$ 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](https://opam.ocaml.org/doc/Install.html) by invoking +```powershell +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`: +```bash +$ 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: + +```bash +$ 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. diff --git a/src/2-tutorial.md b/src/2-tutorial.md index 2685c4e..9dabc6c 100644 --- a/src/2-tutorial.md +++ b/src/2-tutorial.md @@ -324,8 +324,8 @@ 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 scope for our new two-brackets -tax computation. +First, let us start with the data structure and new scope for our new +two-brackets tax computation. ```catala # This structure describes the parameters of a tax computation formula that @@ -391,79 +391,177 @@ 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. -## Scope inclusion +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. -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. As mentioned before, -Catala's scope can also be thought of as big functions. And these big functions -can call each other, which is what we'll see in the below article. +> #### 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. -### Article 5 +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: -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. - -```catala-metadata -declaration scope NewIncomeTaxComputation: - two_brackets scope TwoBracketsTaxComputation - # This line says that we add the item two_brackets to the context. +```catala +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 ``` -```catala -scope NewIncomeTaxComputation : - # Since the subscope "two_brackets" is like a big function we can call, - # we need to define its arguments. This is done below: - definition two_brackets.brackets equals TwoBrackets { - -- breakpoint: $100,000 - -- rate1: 20% - -- rate2: 40% - } - # By defining the input variable "brackets" of the subscope "two_brackets", - # we have changed how the subscope will execute. The subscope will execute - # with all the values defined by the caller, then compute the value - # of its other variables. - - definition income_tax equals two_brackets.tax_formula of individual.income - # After the subscope has executed, you can retrieve results from it. Here, - # we retrieve the result of variable "tax_formula" of computed by the - # subscope "two_brackets". It's up yo you to choose what is an input and - # an output of your subscope; if you make an inconsistent choice, the - # Catala compiler will warn you. -``` +`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: -Now that we've successfully defined our income tax computation, the legislator -inevitably comes to disturb our beautiful and regular formulas to add a special -case! The article below is a really common pattern in statutes, and let's -see how Catala handles it. +> ```catala +> 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`: -### Article 6 +> ```catala +> 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 +> ``` -Individuals earning less than $10,000 are exempted of the income tax mentioned -at article 1. +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: -```catala -scope NewIncomeTaxComputation: - # Here, we simply define a new conditional definition for "income tax" - # that handles the special case. - definition income_tax under condition - individual.income <= $10,000 - consequence equals $0 - # What, you think something might be wrong with this? Hmmm... We'll see - # later! +```text +┌─[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 ``` -That's it! We've defined a two-brackets tax computation with a special case -simply by annotating legislative article by snippets of Catala code. -However, attentive readers may have caught something weird in the Catala -translation of articles 5 and 6. What happens when the income of the individual -is lesser than $10,000? Right now, the two definitions at articles 5 and 6 -for income_tax apply, and they're in conflict. +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. +> +> ```catala +> 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. +> +> ```catala +> 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. +> +> ```catala +> scope IncomeTaxComputation: +> exception article_5 definition income_tax under condition +> individual.income <= $10,000 +> consequence equals $0 +> ``` -This is a flaw in the Catala translation, but the language can help you -find this sort of errors via simple testing or -even formal verification. Let's start with the testing. +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 \ No newline at end of file