Modules externes
Les modules externes sont un moyen d’intégrer une logique externe dans un projet Catala. Par exemple, si un programme Catala nécessite une logique qui est trop lourde (ou tout simplement impossible) à exprimer en Catala, on peut se rabattre sur des modules externes pour les implémenter dans le langage cible du backend souhaité – par exemple, C, Java, OCaml. Les utilisateurs sont tenus de remplir l’implémentation du module externe pour chaque cible backend souhaitée. Par exemple, si un projet ne cible que la compilation vers Java, seule une implémentation de module externe Java doit être présente.
Cependant, pour interpréter un programme Catala (clerk run ou clerk test
sans spécifier l’option --backend), l’implémentation du module externe OCaml
est requise.
Déclarer des modules externes
Certains modules contiennent une logique qui ne peut pas être implémentée en
Catala. C’est normal, puisque Catala est un langage dédié (DSL) et non un
langage de programmation généraliste. Cela signifie que les fonctionnalités de
Catala sont intentionnellement limitées ; par exemple, vous ne pouvez pas écrire
de fonctions récursives ou manipuler des chaînes de caractères
en Catala. Mais parfois, vous avez besoin d’appeler depuis un module Catala
régulier une fonction contenant ce morceau de logique non implémentable. C’est le
but du module externe. Un module externe en Catala est un fichier de code source
Catala contenant un module avec l’annotation externe, comme ceci :
> Module Foo externe
```catala-metadata
déclaration structure Période:
donnée date_début contenu date
donnée date_fin contenu date
déclaration mois_dans_période contenu liste de date
dépend de période contenu Période
```
Ce module externe déclare un type (Période), ainsi qu’une fonction de haut
niveau (mois_dans_période), mais cette dernière n’a pas d’implémentation !
C’est voulu car les modules externes ne doivent pas contenir de code Catala,
mais simplement des déclarations de types, des déclarations de champs
d’application et des déclarations de constantes et de fonctions de haut niveau.
Ces déclarations (à l’intérieur des blocs ```catala-metadata) exposent une
interface publique pour le module externe, qui peut être utilisée par d’autres
modules.
Cependant, pour faire fonctionner tout cela en pratique, vous devrez toujours implémenter le module externe en OCaml (pour l’interpréteur) et dans votre langage cible. Voir la section de référence pertinente.
Implémenter des modules externes
Les modules externes doivent correspondre précisément à l’interface attendue par le code Catala.
Les modules externes sont très dépendants des détails des backends implémentés par Catala. Vous pouvez vous attendre à ce que des mises à jour du code externe soient nécessaires avec les prochaines versions du compilateur.
Pour aider à cela, la commande catala supporte un drapeau --gen-external qui
générera une implémentation modèle dans le backend donné :
$ catala ocaml --gen-external foo.catala_fr
┌─[RESULT]─
│ Generated template external implementations:
│ "foo.template.ml"
│ "foo.template.mli"
└─
Renommez les fichiers (en supprimant la partie .template), et éditez-les pour
remplacer les espaces réservés d’erreur Impossible par votre implémentation.
- Les définitions de types, l’interface et les parties d’enregistrement du module doivent être laissées inchangées
- Procédez de la même manière pour chaque backend vers lequel votre projet sera compilé. Une implémentation OCaml est requise pour exécuter l’interpréteur Catala sur le projet, et fortement recommandée.
Le runtime Catala s’attend à ce que les appels de fonction soient “purs” : un appel à une fonction donnée ne doit dépendre d’aucun contexte. En d’autres termes, appeler la même fonction avec les mêmes arguments devrait toujours donner le même résultat, et il est déconseillé de stocker un état persistant dans un module externe.
Lors de la compilation, le système de construction Clerk récupérera automatiquement l’implémentation fournie.
En cas de changements dans l’interface Catala (ou lors de la mise à jour du
compilateur Catala), vous devez réexécuter la commande --gen-external et
résoudre les divergences entre votre implémentation et le nouveau fichier
.template.
Types externes
Il est parfois utile, lorsqu’on définit des modules externes, de manipuler des valeurs qui ne peuvent être définies en Catala pur. Les types externes le permettent:
déclaration type TypeAbstrait: externe
Le type TypeAbstrait défini ici peut être manipulé en Catala (utilisé comme
membre de structures, entrées de champ d’application, etc.) mais reste opaque
pour le reste du programme: seules les fonctions définies à l’intérieur du
module externe seront en mesure d’en observer le contenu.
L’usage des types externes nécessite cependant des précautions particulières:
- Suivant le backend utilisé, l’implémentation concrète du type doit se plier
aux règles de Catala pour ce backend. Par exemple, un type externe implémenté
en Java doit implémenter l’interface
CatalaValue. - Comme mentionné ci-dessus, Catala est basé sur un modèle fonctionnel:
n’utilisez pas de types mutables. Les modifications en-place de valeurs
sont interdites. Supposons par exemple un type externe
Ensemblequi permette de stocker des collections de valeurs: la fonction ajoutant un élément à l’ensemble devra renvoyer un nouvel ensemble comportant le nouvel élément, et en aucun cas modifier l’ensemble passé en argument.