tag:linuxfr.org,2005:/users/gipoissonLinuxFr.org : les contenus de gipoisson2020-06-18T14:29:13+02:00/favicon.pngtag:linuxfr.org,2005:Diary/391612020-05-20T14:53:03+02:002020-05-20T14:53:03+02:00Dhall, une réponse au problème de configurationLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#toc-pr%C3%A9sentation-">Présentation </a><ul>
<li><a href="#toc-condens%C3%A9-en-deux-ou-trois-paragraphes">Condensé en deux ou trois paragraphes</a></li>
<li><a href="#toc-caract%C3%A9ristiques-conceptuelles">Caractéristiques conceptuelles</a></li>
<li><a href="#toc-syntaxe-et-exemple-plut%C3%B4t-d%C3%A9pouill%C3%A9">Syntaxe et exemple plutôt dépouillé</a></li>
</ul>
</li>
<li><a href="#toc-impl%C3%A9mentations-et-interop%C3%A9rabilit%C3%A9">Implémentations et interopérabilité</a></li>
<li><a href="#toc-approfondir-le-sujet">Approfondir le sujet</a></li>
</ul>
<p>Cher Journal,</p>
<p>Ne te fie pas à l’intitulé, « le problème » n’est qu’un condensé fourre-tout pour ce qui est connu en tant que <em>“Software Configuration Management issues”</em> dans la <em>lingua veicolare</em> de la sphère informatique. Quant à Dhall, il s’agit d’un langage spécialisé dans la gestion de configurations<sup id="fnref1"><a href="#fn1">1</a></sup>.</p>
<h2 id="toc-présentation-">Présentation </h2>
<h3 id="toc-condensé-en-deux-ou-trois-paragraphes">Condensé en deux ou trois paragraphes</h3>
<p>Dhall est un outil primordialement destiné à programmer les fichiers de configuration. En acronymes de moins de cinq lettres, tu as certainement entendu parler de formats comme INI, XML, JSON, YAML, TOML. Ils ont le point commun de ne pas être programmable. Différents degrés de programmabilité présentent différents atouts, entre autres la réduction des répétitions, la réutilisation ailleurs de portions produites dans un endroit initial, …</p>
<p>Un fichier <code>.dhall</code> à la fois exprime de la configuration et constitue une expression dans le langage de programmation Dhall. Cette expression sera <em>in fine</em> réduite, normalisée, après un processus d’évaluation, lequel processus est garanti de prendre cours pendant un temps fini. Par conséquent, peu importe l’expressivité, la technicité, … d’une expression Dhall, elle finira par être aplatie sous forme de valeurs <em>basiques</em>, id est telles que définies par le standard. Les expressions sont typées et les types standard dans la version actuelle sont sommairement listés ici-bas.</p>
<ol>
<li>
<code>Bool</code> : booléens</li>
<li>
<code>Natural</code> : nombres entiers naturels (positifs, non-signés)</li>
<li>
<code>Integer</code> : nombres entiers signés</li>
<li>
<code>Double</code> : nombres décimaux</li>
<li>
<code>Text</code> : du texte</li>
<li>
<code>List</code> : tableaux, listes chaînées de valeurs partageant le même type</li>
<li>
<code>Optional</code> : variants, sommes</li>
<li>
<code>Record</code> : produits</li>
</ol>
<p>En plus des types, le langage</p>
<ul>
<li>permet d’exprimer des expressions intermédiaires sous forme de <code>let … in …</code>.</li>
<li>est muni d’une bibliothèque standard.</li>
</ul>
<h3 id="toc-caractéristiques-conceptuelles">Caractéristiques conceptuelles</h3>
<p>L’auteur originnel est Gabriel GONZALEZ (<a href="http://www.haskellforall.com/">lien en HTTP</a>) et certains choix reflètent des idées qui lui sont chères. Néanmoins, beaucoup d’autres personnes ont contribué au langage depuis son inception et cette sous-section se contentera de faire des constats sans chercher à les attribuer à Gabriel et ali.</p>
<p>Dhall a un typage fort et statique : une expression est correctement typée ou ne l’est pas et cela se voit très tôt ; il n’y a pas de contournements possibles pour essayer de retarder la vérification de types. Cela étant dit, tout est abondamment documenté, même les erreurs. La manière dont le langage évolue (quelle variété de typage, quelle fonction, … ajouter) ne dépend pas tant des avancées dans les systèmes de types, mais plutôt de la motivation plus ou moins avouée de la communauté autour : remplacer les systèmes de configurations non-programmables les plus en vogue, nommément JSON, YAML et TOML. Eh oui, mon journal, Gabriel <em>et ali</em> ont des opinions ! Ces soubassements dévoilés, il sera plus compréhensible de voir le foisonnement d’exemples liés à JSON et YAML dans diverses pages de la documentation <em>upstream</em>.</p>
<p>Un des choix prépondérant du langage est la limitation explicite des opérations possibles sur du texte. Plus précisément, une seule opération est permise : la concaténation. Ainsi ne peut-on pas comparer deux valeurs de types <code>Text</code> ou en calculer la taille en caractères. Encore une fois, derrière ce choix se trouve l’accent mis sur la capacité de bannir des catégories d’erreurs dès la vérification de types.</p>
<p>Un autre choix de poids garantit que tout opérateur</p>
<ol>
<li>est associatif, c'est-à-dire qu’il est superflu de préciser la priorité selon laquelle ses arguments seront évalués; par exemple, si <code>a</code> est de type <code>Natural</code> (<code>a : Natural</code>) ainsi que <code>b : Natural</code> et <code>c : Natural</code>, alors on peut additionner les trois avec l’opérateur <code>+</code> en faisant <code>(a + b) + c</code> ou <code>a + (b + c)</code> et les deux expressions équivalent à <code>a + b + c</code>.</li>
<li>est muni d’une valeur nulle, un élément neutre, un zéro, valable dans toute l’étendue du type pour lequel il est défini. Exemples sous la notation (opérateur, zéro):</li>
</ol>
<ul>
<li>
<code>(*, 1)</code> dans <code>Natural</code> et <code>Integer</code>,</li>
<li>
<code>(∧, {=})</code> dans <code>Record</code>,</li>
<li>
<code>(==, True)</code> dans <code>Boolean</code>.</li>
</ul>
<p>Derrière ces restrictions se trouve la nécessité de garantir que le résultat de l’application d’un opérateur aura le même type que les arguments qui y auront contribué ; c’est une nécessité propre à Dhall … J’ai mentionné plus haut qu’il est impossible de comparer deux valeurs de type <code>Text</code> ; cela est ainsi parce que <code>(a : Text) == (b : Text)</code> donnerait un <code>c : Bool</code>, une valeur de type différent de celui des valeurs de départ.</p>
<p>Puisqu’on parle de fichiers de configuration, il sautera aux yeux que ces restrictions sont incompatibles avec la manière dont on conçoit les tableaux associatifs ou dictionnaires, structures de données omniprésentes dans les configurations : avant de modifier de telles structures, des comparaisons d’égalité s’imposent. Un examen attentif du type <code>Record</code> couplé avec des fonctions de la bibliothèque standard permettent de lever l’apparente rigidité du langage.</p>
<h3 id="toc-syntaxe-et-exemple-plutôt-dépouillé">Syntaxe et exemple plutôt dépouillé</h3>
<p>Persée, Dictys, et Méduse forment une équipe chargée d’assurer les cours à distance d’une classe covid-confinée. Cassiopée est en charge de l’infrastructure sur laquelle tout cela reposera et, pour diverses raisons, décide que chaque membre du triptyque devrait avoir une configuration afin de garantir les conditions suivantes.</p>
<ul>
<li>Pour un jour donné, indiquer une plage horaire pendant laquelle ses activités seront accomplies.</li>
<li>Énoncer d’emblée quels sont les thèmes que son cours abordera.</li>
<li>Déposer des documents supplémentaires potentiels et laisser Cassiopée en faire la distribution à l’audience.</li>
</ul>
<p>Cassiopée est bien sûr Dhall-iste, découpe temporellement les choses en termes de semaines et prototype ses actions ainsi (juste un extrait) :</p>
<pre><code class="haskell"><span class="c1">-- Fichier: a.dhall</span>
<span class="p">{</span>
<span class="n">semaine</span> <span class="kt">:</span> <span class="kt">Natural</span>
<span class="p">,</span> <span class="n">personnel</span> <span class="kt">:</span> <span class="kt">List</span> <span class="kt">Text</span>
<span class="p">}</span></code></pre>
<p>Comme indiqué précédemment, le type d’une valeur est indiqué par deux points. Les annotations de types ne sont pas partout requis, Dhall inférant les types pour tous les cas non-ambigus ou non-spéciaux. Tout ce qui est dans ce fichier <code>a.dhall</code> est facultatif à Dhall mais est aussi valide. <code>semaine</code> est un nombre entier naturel, <code>personnel</code> un tableau dans lequel chaque élément est une valeur textuelle. Remarquons que</p>
<ul>
<li>l’expression finale est une valeur anonyme de type <code>Record</code>, ce qu’indique les accolades ;</li>
<li>l’indentation est laissée à l’appréciation de tout le monde ;</li>
<li>les commentaires mono-lignes sont préfixées par deux tirets successifs.</li>
</ul>
<p>Pour évaluer le fichier, utilisons par exemple le binaire <code>dhall</code> dans le Shell (Bash de ce côté ; les sorties sont en commentaires) :</p>
<pre><code class="bash">dhall --file <span class="s1">'a.dhall'</span>
<span class="c1"># { personnel : List Text, semaine : Natural }</span></code></pre>
<p>Au lieu de types, traitons de valeurs.</p>
<pre><code class="haskell"><span class="c1">-- Fichier: b.dhall</span>
<span class="kr">let</span> <span class="n">per</span> <span class="ow">=</span> <span class="s">"Persée"</span>
<span class="kr">let</span> <span class="n">dic</span> <span class="ow">=</span> <span class="s">"Dictys"</span> <span class="kt">:</span> <span class="kt">Text</span>
<span class="kr">in</span> <span class="p">{</span>
<span class="n">semaine</span> <span class="ow">=</span> <span class="mi">20</span>
<span class="p">,</span> <span class="n">personnel</span> <span class="ow">=</span> <span class="p">[</span><span class="n">per</span><span class="p">,</span> <span class="n">dic</span><span class="p">,</span> <span class="s">"Méduse"</span><span class="p">]</span>
<span class="p">}</span></code></pre>
<p><code>dhall</code> dit que nous sommes toujours dans les clous:</p>
<pre><code class="bash">dhall --file <span class="s1">'b.dhall'</span>
<span class="c1"># { personnel = [ "Persée", "Dictys", "Méduse" ], semaine = 20 }</span>
dhall --annotate --file <span class="s1">'b.dhall'</span>
<span class="c1"># { personnel = [ "Persée", "Dictys", "Méduse" ], semaine = 20 }</span>
<span class="c1"># : { personnel : List Text, semaine : Natural }</span></code></pre>
<p>Constatons que l’évaluation a normalisé les entrées puisqu’en plus de la suppression du commentaire, les indirections <code>let … in</code> ont également disparues. L’expression reste une valeur anonyme de type <code>Record</code>. <code>let</code> introduit des variables visibles à l’intérieur de <code>in</code>.</p>
<p>Pour en finir avec cette démo, parlons des fonctions. La syntaxe à la Curry, le calcul lambda — ou toute autre expression par laquelle, ô journal !, tu serais déjà accoutumé à ce qui suit — alors, cette syntaxe permet de considérer que n’importe quelle fonction possède un seul argument. S’il y en a effectivement un, tant mieux, on l’applique: <code>fct arg</code> : ceci est un appel de la fonction <code>fct</code> sur le paramètre <code>arg</code>. S’il y en a plusieurs, on applique le premier argument à la fonction et on retourne une fonction anonyme qui prendra le reste des arguments et continuera le processus d’applications jusqu’à l’aboutissement d’une forme aplatie, normalisée. Le Robert me dit qu’un péquin est une personne quelconque, une personne lambda; par analogie, une fonction quelconque, anonyme, est aussi appelée fonction lambda. Voyons où ce détour nous mène :</p>
<pre><code class="haskell"><span class="c1">-- Fichier: c.dhall</span>
<span class="kr">let</span> <span class="n">precedemment</span> <span class="ow">=</span> <span class="o">./</span><span class="n">b</span><span class="o">.</span><span class="n">dhall</span>
<span class="kr">let</span> <span class="n">dossier_global</span> <span class="ow">=</span> <span class="s">"/home/cours/"</span>
<span class="kr">let</span> <span class="n">creer_dossier</span> <span class="ow">=</span> <span class="n">λ</span><span class="p">(</span><span class="n">val</span> <span class="kt">:</span> <span class="kt">Text</span><span class="p">)</span> <span class="err">→</span> <span class="n">dossier_global</span> <span class="o">++</span> <span class="n">val</span> <span class="o">++</span> <span class="s">"/"</span>
<span class="kr">let</span> <span class="n">ajouter_theme</span> <span class="ow">=</span> <span class="n">λ</span><span class="p">(</span><span class="n">ind</span> <span class="kt">:</span> <span class="kt">Text</span><span class="p">)</span> <span class="err">→</span> <span class="n">λ</span><span class="p">(</span><span class="n">thm</span> <span class="kt">:</span> <span class="kt">Text</span><span class="p">)</span> <span class="err">→</span> <span class="n">creer_dossier</span> <span class="n">ind</span> <span class="o">++</span> <span class="n">thm</span>
<span class="kr">in</span>
<span class="p">{</span> <span class="n">thematiques</span> <span class="ow">=</span>
<span class="p">[</span> <span class="n">creer_dossier</span> <span class="s">"Persée"</span>
<span class="p">,</span> <span class="n">creer_dossier</span> <span class="s">"Dictys"</span>
<span class="p">,</span> <span class="n">ajouter_theme</span> <span class="s">"Méduse"</span> <span class="s">"Que racontait Zarathoustra ?"</span> <span class="p">]</span>
<span class="p">}</span> <span class="err">∧</span> <span class="n">precedemment</span></code></pre>
<ul>
<li>
<code>precedemment</code> a capturé une ressource, une URI. Ici, c’était un fichier indiqué par un chemin relatif. La ressource pouvait aussi être adressée par un chemin absolu ou une URL sur le WWW ; dans ce dernier cas, l’évaluation de l’expression aurait nécessité de la connexion Internet.</li>
<li>
<code>creer_dossier</code> est un synonyme à la fonction lambda qui prend un seul argument et y applique deux concaténations grâce à l’opérateur <code>++</code>.</li>
<li>
<code>ajouter_theme</code> est un synonyme à une autre fonction anonyme qui prend deux arguments et fait usage de la fonction <code>creer_dossier</code>.</li>
<li>Le <code>Record</code> anonyme en cours de création est concaténée au <code>Record</code> créé ailleurs, dans le fichier <code>b.dhall</code>. La fusion des deux est accomplie par l’application de l’opérateur <code>∧</code>.</li>
<li>L’usage de l’Unicode est laissée à l’appréciation de tout le monde.</li>
<li>L’expression finale est celle-ci:</li>
</ul>
<pre><code class="bash">dhall --file <span class="s1">'c.dhall'</span>
<span class="c1"># { personnel = [ "Persée", "Dictys", "Méduse" ]</span>
<span class="c1"># , semaine = 20</span>
<span class="c1"># , thematiques =</span>
<span class="c1"># [ "/home/cours/Persée/"</span>
<span class="c1"># , "/home/cours/Dictys/"</span>
<span class="c1"># , "/home/cours/Méduse/Que racontait Zarathoustra ?"</span>
<span class="c1"># ]</span>
<span class="c1"># }</span>
dhall <span class="nb">type</span> --file <span class="s1">'c.dhall'</span>
<span class="c1"># { personnel : List Text, semaine : Natural, thematiques : List Text }</span></code></pre>
<p>Bref, nous avons obtenu un tout-bête fichier de configuration. Qu’en est-il des plages horaires et les plans sur la comète de Cassiopée ? Ah, journal !, tu l’as vu venir : « Cela est laissé en tant qu’exercice … »</p>
<p>Cette sous-section a commencé en attribuant toutes les choses qui allaient apparaître à Cassiopée ; je tiens à préciser que tout malentendu, toute imprécision ne sont dus qu’à moi ; Cassiopée, Dhall, … sont quittes.</p>
<h2 id="toc-implémentations-et-interopérabilité">Implémentations et interopérabilité</h2>
<p>Un langage qui ne cache pas vouloir en remplacer d’autres doit se doter d’outils de migration. Sans même parler de migration, ces outils permettent de juste satisfaire la curiosité de personnes qui sont habituées à autre chose. Dans cette optique, il existe un certain nombre de binaires et bibliothèques qui effectuent la conversion <code>dhall-to-FOO</code> ou <code>Foo-to-dhall</code>. En témoin ces deux-là :</p>
<ul>
<li>YAML: <code>dhall-to-yaml --file 'c.dhall'</code>
</li>
</ul>
<pre><code class="yaml"><span class="l l-Scalar l-Scalar-Plain">personnel</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="s">"Persée"</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">Dictys</span>
<span class="p p-Indicator">-</span> <span class="s">"Méduse"</span>
<span class="l l-Scalar l-Scalar-Plain">semaine</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">20</span>
<span class="l l-Scalar l-Scalar-Plain">thematiques</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="s">"/home/cours/Persée/"</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">/home/cours/Dictys/</span>
<span class="p p-Indicator">-</span> <span class="s">"/home/cours/Méduse/Que</span><span class="nv"> </span><span class="s">racontait</span><span class="nv"> </span><span class="s">Zarathoustra ?"</span></code></pre>
<ul>
<li>JSON: <code>dhall-to-json --file 'c.dhall'</code>
</li>
</ul>
<pre><code class="json"><span class="p">{</span>
<span class="nt">"personnel"</span><span class="p">:</span> <span class="p">[</span>
<span class="s2">"Persée"</span><span class="p">,</span>
<span class="s2">"Dictys"</span><span class="p">,</span>
<span class="s2">"Méduse"</span>
<span class="p">],</span>
<span class="nt">"semaine"</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span>
<span class="nt">"thematiques"</span><span class="p">:</span> <span class="p">[</span>
<span class="s2">"/home/cours/Persée/"</span><span class="p">,</span>
<span class="s2">"/home/cours/Dictys/"</span><span class="p">,</span>
<span class="s2">"/home/cours/Méduse/Que racontait Zarathoustra ?"</span>
<span class="p">]</span>
<span class="p">}</span></code></pre>
<p>Il y a même un <code>dhall-to-yaml-ng</code> produisant une autre variété de YAML.</p>
<p>Dhall a un standard et des implémentations diverses. À cette date, il y en a de matures en Clojure, Eta, Haskell, Java, Go, Nix, Ruby, et Rust.</p>
<p>Quant à la portabilité du langage sur des architectures matérielles, j’invoquerai un seul mot: <strong>Go</strong>. Une norme qui est implémentée dans ce langage se voit de facto portée sur une galaxie d’architectures. Je te vois venir, journal ! Je suis d’accord avec toi sur les problématiques liées à la taille et la puissance de Google ; à vrai dire, mon Go-innumérisme est même immensurable. Cependant, le binaire <code>go</code> qui se trimbale sur cette machine dit des choses que je ne peux pas ignorer :</p>
<pre><code class="bash"><span class="c1"># Combien d’architectures supporte golang?</span>
go tool dist list <span class="p">|</span> wc --lines
<span class="c1"># 43</span></code></pre>
<p>Dans la liste des implémentations, il y a Nix, un langage et ensembles d’outils qui sont, disons, des super-ensembles de Dhall à maints égards. Ça mérite une mention mais je préfère ne pas ouvrir un autre potentiel front après Go …</p>
<h2 id="toc-approfondir-le-sujet">Approfondir le sujet</h2>
<p>Voici une collection de liens triés subjectivement par ordre décroissant de pertinence :</p>
<ul>
<li><a href="https://dhall-lang.org">Site web du langage</a></li>
<li><a href="https://docs.dhall-lang.org/references">Spécification formelle</a></li>
<li><a href="https://github.com/dhall-lang/dhall-lang/blob/9358ffa9c8241b0d8446d46010318758b0df38cb/standard/dhall.abnf">Syntaxe en ABNF</a></li>
<li>Standard ou norme de façon exhaustive: le dossier parent du précédent document</li>
<li><a href="https://docs.dhall-lang.org/references/Prelude.html">Bibliothèque standard</a></li>
<li><a href="https://docs.dhall-lang.org/howtos/How-to-integrate-Dhall.html#language-support">Implémentations du standard</a></li>
</ul>
<p>Gabriel dira à qui veut entendre que Dhall est Turing-incomplet. Pourquoi insister ? <a href="http://www.haskellforall.com/2020/01/why-dhall-advertises-absence-of-turing.html">Voici en HTTP</a> ce qu’il a à dire sur ce sujet.</p>
<p>Par ailleurs, d’aucuns diront que YAML est bien comme il est, <a href="https://www.arp242.net/yaml-config.html">n’est-ce pas ?</a> Et si on adoptait une approche diamétralement opposée en se débarrassant complètement des fichiers de configuration ? Il se trouve que nous avons des langages de programmation pour … programmer, notamment les fichiers de configurations. Et toi, journal, penses-tu que, <em>sic</em>, <a href="https://beepb00p.xyz/configs-suck.html">“[…] configs suck?”</a></p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p>J’ai commis ce texte sous <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC by SA 4.0</a> <a href="#fnref1">↩</a></p>
</li>
</ol>
</div>
<div><a href="https://linuxfr.org/users/gipoisson/journaux/dhall-une-reponse-au-probleme-de-configuration.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/120500/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/gipoisson/journaux/dhall-une-reponse-au-probleme-de-configuration#comments">ouvrir dans le navigateur</a>
</p>
gipoissonhttps://linuxfr.org/nodes/120500/comments.atom