tag:linuxfr.org,2005:/users/ellyLinuxFr.org : les contenus de Lizzie Crowdagger2014-04-02T19:31:46+02:00/favicon.pngtag:linuxfr.org,2005:News/351522014-04-01T06:36:06+02:002016-05-02T15:16:10+02:00Sortie de Clojure 1.6Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Le 25 mars, Clojure est sorti en version 1.6, l'occasion de se pencher un peu sur ce langage. </p>
<p>Clojure est un langage de programmation fonctionnel dérivé de Lisp tournant au-dessus de la Machine Virtuelle Java, des ports existant également pour Javascript et pour le <em>Common Language Runtime</em> de .NET.</p></div><ul><li>lien nᵒ 1 : <a title="http://clojure.org/" hreflang="en" href="https://linuxfr.org/redirect/89874">Site officiel</a></li><li>lien nᵒ 2 : <a title="https://github.com/clojure/clojure/blob/master/changes.md" hreflang="en" href="https://linuxfr.org/redirect/89875">Notes de version</a></li><li>lien nᵒ 3 : <a title="http://clojure-doc.org/" hreflang="en" href="https://linuxfr.org/redirect/89876">Clojure-doc (site de documentation, contenant notamment des tutoriaux)</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#pr%C3%A9sentation-du-langage">Présentation du langage</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#un-peu-de-syntaxe">Un peu de syntaxe</a></li>
<li>
<a href="#particularit%C3%A9s-de-clojure">Particularités de Clojure</a><ul>
<li><a href="#structures-de-donn%C3%A9es">Structures de données</a></li>
<li><a href="#un-accent-sur-limmuabilit%C3%A9">Un accent sur l'immuabilité</a></li>
<li><a href="#du-sucre-syntaxique">Du sucre syntaxique</a></li>
<li><a href="#interop%C3%A9rabilit%C3%A9-avec-java">Interopérabilité avec Java</a></li>
<li><a href="#pas-vraiment-de-mod%C3%A8le-objet">Pas (vraiment) de modèle objet</a></li>
</ul>
</li>
<li><a href="#changements-apport%C3%A9s-par-la-version-16">Changements apportés par la version 1.6</a></li>
</ul><h2 id="présentation-du-langage">Présentation du langage</h2>
<p>Conçu par Rich Hickey, et présenté pour la première fois en 2007, Clojure est un langage dont l'objectif est d'être une variante moderne de Lisp. S'il garde certaines des spécificités de cette famille de langages (considérer le code comme des données, ce qui permet notamment un usage sophistiqué de macros), il a comme particularité d'avoir une approche plus orientée vers la programmation fonctionnelle (décourageant l'utilisation de variables muables) et vers la concurrence. Par ailleurs, Clojure est conçu pour être en « symbiose » avec son hôte (dans la plupart des cas, la machine virtuelle Java) et permet d'utiliser facilement le code existant et de mêler au sein d'un même projet du code écrit en Java et en Clojure.</p>
<h2 id="installation">Installation</h2>
<p>La manière la plus simple d'utiliser Clojure est d'utiliser <a href="http://leiningen.org/">Leiningen</a>. Cet outil (en ligne de commande) permettra entre autres choses de récupérer les dépendances nécessaires (dont Clojure lui-même, qui prend la forme d'une bibliothèque Java), de créer l'arborescence pour un nouveau projet et de compiler ce projet. </p>
<p>Cet outil permet également (via <code>lein repl</code>) de lancer un REPL (<em>Read-Eval-Print-Loop</em>) qui permet de manipuler interactivement du code. Cf. le tutorial <a href="//linuxfr.org/users/philippemc/journaux/clojurer-des-regexps-avec-java-en-lisp">Clojurer des regexps avec Java, en Lisp :)</a> qui présente un peu plus cet aspect-là.</p>
<p>Pour tester Clojure sans avoir à installer quoi que ce soit sur sa machine, il est également possible d'utiliser le site <a href="http://tryclj.com/">Try Clojure</a>, qui fournit un REPL accessible par le web.</p>
<h2 id="un-peu-de-syntaxe">Un peu de syntaxe</h2>
<p>La syntaxe de Clojure ne dépaysera pas les habitués de Lisp, ou de ses variantes. </p>
<pre><code class="clojure"><span class="p">(</span><span class="nb">println </span><span class="s">"Hello, world !"</span><span class="p">)</span>
<span class="c1">;; affiche "Hello, world !"</span>
<span class="p">(</span><span class="nb">+ </span><span class="mi">2</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; renvoie 4</span>
<span class="p">(</span><span class="nb">* </span><span class="mi">3</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; renvoie 6</span></code></pre>
<p>Clojure est <a href="http://fr.wikipedia.org/wiki/Typage_dynamique">dynamiquement typé</a> : c'est au moment de l'exécution qu'on vérifie qu'une valeur est bien du type attendu. Comme le langage tourne sur la machine virtuelle Java, un certain nombre de types de base, comme les entiers ou les chaînes de caractères, viennent directement de Java, comme on peut le constater avec la fonction <code>type</code> :</p>
<pre><code class="clojure"><span class="p">(</span><span class="nf">type</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; renvoie java.lang.Long</span>
<span class="p">(</span><span class="nf">type</span> <span class="s">"Hello, world !"</span><span class="p">)</span>
<span class="c1">;; renvoie java.lang.String</span></code></pre>
<p>Un petit mot sur la syntaxe, directement héritée de Lisp. Dans tous les exemples ci-dessus, on construit, grâce aux parenthèses, une liste contenant un certain nombre d'éléments. Cette liste est ensuite évaluée, et le premier élément est interprété comme la fonction à appeler : dans le premier cas, <code>println</code>, dans les suivants <code>+</code> et <code>*</code> (qui ne sont pas considérés comme des caractères spéciaux). Les autres éléments de la liste correspondent aux arguments donnés à cette fonction.</p>
<p>Pour créer une liste, il suffit de demander à ce qu'elle ne soit pas évaluée, <em>via</em> <code>quote</code>, ou le caractère spécial <code>'</code> :</p>
<pre><code class="clojure"><span class="p">(</span><span class="k">quote </span><span class="p">(</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">5</span><span class="p">))</span>
<span class="c1">;; renvoie (1 2 3 4 5)</span>
<span class="o">'</span><span class="p">(</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">5</span><span class="p">)</span>
<span class="c1">;; identique</span></code></pre>
<p>C'est le principe fondateur de Lisp (le nom <em>Lisp</em> vient de <em>LISt Processing</em>) : le code est constitué de données, c'est à dire que tout le code qu'on écrit est en réalité constitué de listes qui vont être évaluées. Il s'agit d'une structure assez simple, celle de liste simplement chaînée, sur laquelle on peut effectuer un certain nombre d'opérations basiques : </p>
<ul>
<li>
<code>(cons x une-liste)</code> ajoute l'élément <code>x</code> au début de la liste <code>une-liste</code> ;</li>
<li>
<code>(first une-liste)</code> retourne le premier élément d'<code>une-liste</code> (correspond à <code>car</code> dans Lisp) ;</li>
<li>
<code>(rest une-liste)</code> retourne une liste comprenant tous les éléments d'<code>une-liste</code>, sauf le premier (correspond à <code>cdr</code> dans Lisp).</li>
</ul><p>Quelques exemples :</p>
<pre><code class="clojure"><span class="p">(</span><span class="k">def </span><span class="nv">une-liste</span> <span class="o">'</span><span class="p">(</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span> <span class="mi">5</span><span class="p">))</span>
<span class="c1">;; définit une variable, une-liste, contenant (1 2 3 4 5)</span>
<span class="p">(</span><span class="nb">first </span><span class="nv">une-liste</span><span class="p">)</span>
<span class="c1">;; renvoie 1</span>
<span class="p">(</span><span class="nb">rest </span><span class="nv">une-liste</span><span class="p">)</span>
<span class="c1">;; renvoie (2 3 4 5)</span>
<span class="p">(</span><span class="nb">cons </span><span class="mi">0</span> <span class="nv">une-liste</span><span class="p">)</span>
<span class="c1">;; renvoie (0 1 2 3 4 5)</span></code></pre>
<h2 id="particularités-de-clojure">Particularités de Clojure</h2>
<h3 id="structures-de-données">Structures de données</h3>
<p>En Lisp, la liste est donc <em>la</em> structure hégémonique que l'on retrouve partout et qui est l'élément de syntaxe principal du langage (les fameuses parenthèses). Clojure garde le même principe, mais avec quelques nuances. Ainsi, pour déclarer une fonction :</p>
<pre><code class="clojure"><span class="p">(</span><span class="kd">defn </span><span class="nv">doubler</span> <span class="p">[</span><span class="nv">x</span><span class="p">]</span>
<span class="p">(</span><span class="nb">* </span><span class="mi">2</span> <span class="nv">x</span><span class="p">))</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; renvoie 4</span></code></pre>
<p>Les crochets autour des paramètres de la fonction (en l'occurrence, <code>x</code>) ont une signification bien particulière : ils indiquent qu'il ne s'agit pas d'une liste ordinaire, mais d'un vecteur. Clojure se différencie en effet de son aîné en proposant « en dur » d'autres structures de données, comme les vecteurs : </p>
<pre><code class="clojure"><span class="p">[</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">]</span></code></pre>
<p>À première vue, un vecteur peut ne pas sembler très différent d'une liste, mais accéder à un élément ou en ajouter un n'ont pas les mêmes impacts en termes de performances :</p>
<ul>
<li>avec une liste, il est possible d'accéder au premier élément ou d'ajouter un élément en début de liste en temps constant, mais pour accéder au n-ième élément il faudra parcourir la liste depuis le début ;</li>
<li>à l'inverse, avec un vecteur, il est possible d'accéder à n'importe quel élément avec un temps constant ; avec l'implémentation de Clojure, il en est de même pour ajouter un élément à la fin du vecteur.</li>
</ul><p>Clojure fournit également une syntaxe pour les ensembles :</p>
<pre><code class="clojure"><span class="o">#</span><span class="p">{</span><span class="s">"bleu"</span> <span class="s">"rouge"</span> <span class="s">"vert"</span><span class="p">}</span></code></pre>
<p>ainsi que pour les dictionnaires :</p>
<pre><code class="clojure"><span class="p">{</span><span class="mi">1</span> <span class="s">"bleu"</span>
<span class="mi">2</span> <span class="s">"rouge"</span>
<span class="mi">3</span> <span class="s">"vert"</span><span class="p">}</span></code></pre>
<h3 id="un-accent-sur-limmuabilité">Un accent sur l'immuabilité</h3>
<p>À première vue, ces ajouts peuvent être vus comme du simple sucre syntaxique, puisque ces structures de données peuvent également être créées par l'appel à des fonctions, repectivement :</p>
<pre><code class="clojure"><span class="p">(</span><span class="nb">vector </span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1">;; crée un vecteur</span>
<span class="p">(</span><span class="nb">set </span><span class="o">'</span><span class="p">(</span><span class="s">"bleu"</span> <span class="s">"rouge"</span> <span class="s">"vert"</span><span class="p">))</span>
<span class="c1">;; crée un ensemble</span>
<span class="p">(</span><span class="nb">array-map </span><span class="mi">1</span> <span class="s">"bleu"</span> <span class="mi">2</span> <span class="s">"rouge"</span> <span class="mi">3</span> <span class="s">"vert"</span><span class="p">)</span>
<span class="c1">;; crée un dictionnaire</span></code></pre>
<p>Cependant, l'intérêt de ces structures n'est pas uniquement d'avoir du code comportant un peu moins de parenthèses et plus de caractères différents. Leur particularité est qu'à l'instar des listes, ces structures de données sont <a href="http://fr.wikipedia.org/wiki/Structure_de_donn%C3%A9es_persistante">persistantes</a>. Clojure met en effet un fort accent sur l'immuabilité. Par exemple, pour ajouter une valeur à un vecteur, plutôt que de modifier le vecteur lui-même, on va créer un nouveau vecteur, via la fonction <code>conj</code> :</p>
<pre><code class="clojure"><span class="p">(</span><span class="k">def </span><span class="nv">mon-vecteur</span> <span class="p">[</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">])</span>
<span class="p">(</span><span class="nb">conj </span><span class="nv">mon-vecteur</span> <span class="mi">5</span><span class="p">)</span>
<span class="c1">;; renvoie [1 2 3 4 5]</span>
<span class="nv">mon-vecteur</span>
<span class="c1">;; contient toujours [1 2 3 4]</span></code></pre>
<p>Si garantir cette immuabilité pour toutes les structures de données du langage a un coût (en termes de performances), cela présente un certain nombre d'avantages, notamment en programmation concurrente : en n'utilisant que des variables immuables, on n'a pas à se soucier de ce qui peut arriver si un autre <em>thread</em> modifie les données sur lesquelles on est en train de travailler.</p>
<p>Bien entendu, il est parfois indispensable qu’une variable puisse être modifiée. Clojure fait le choix de proposer plusieurs solutions pour ce cas, en fonction des besoins (en termes de concurrence et de parallélisme). Ainsi, si modifier une variable par le biais du mot‐clé <code>def</code> n’entraînera un changement qui ne sera visible qu’à l’intérieur du même <em>thread</em>, il existe également <code>atom</code>, <code>agent</code> et <code>ref</code> qui permettent de partager des données entre plusieurs <em>threads</em>, avec des mécanismes un peu différents :</p>
<ul>
<li>
<code>atom</code> s'assure simplement, comme son nom l'indique, d'une modification atomique de la variable, garantissant qu'elle soit toujours dans un état cohérent. C'est notamment utile lorsqu'une modification de cette variable n'a pas à être coordonnée avec la modification d'autres variables ;</li>
<li>
<code>ref</code> permet une modification coordonnée de plusieurs variables, de manière synchrone ;</li>
<li>
<code>agent</code> permet une modification coordonnée de plusieurs variables, de manière asynchrone.</li>
</ul><h3 id="du-sucre-syntaxique">Du sucre syntaxique</h3>
<p>Clojure fournit également du sucre syntaxique pour permettre une utilisation plus facile de ces structures de données. Ainsi, vecteurs comme dictionnaires peuvent être utilisés comme des fonctions, renvoyant la valeur correspondant au paramètre qui leur est passé :</p>
<pre><code class="clojure"><span class="p">(</span><span class="k">def </span><span class="nv">v</span> <span class="p">[</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">])</span>
<span class="p">(</span><span class="k">def </span><span class="nv">d</span> <span class="p">{</span><span class="mi">1</span> <span class="s">"bleu"</span>
<span class="mi">2</span> <span class="s">"rouge"</span>
<span class="mi">3</span> <span class="s">"vert"</span><span class="p">})</span>
<span class="p">(</span><span class="nf">v</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; renvoie 3</span>
<span class="p">(</span><span class="nf">d</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1">;; renvoie "bleu"</span></code></pre>
<p>En ce qui concerne les dictionnaires, leurs clés correspondent souvent à des mots-clés, des identifiants commençant par le caractère <code>:</code>. Il est également possible d'utiliser ces mots-clés en guise de fonction, afin d'obtenir la valeur correspondante dans le dictionnaire passé en argument :</p>
<pre><code class="clojure"><span class="p">(</span><span class="k">def </span><span class="nv">point</span> <span class="p">{</span><span class="ss">:x</span> <span class="mi">3</span>
<span class="ss">:y</span> <span class="mi">4</span><span class="p">})</span>
<span class="p">(</span><span class="ss">:x</span> <span class="nv">point</span><span class="p">)</span>
<span class="c1">;; renvoie 3</span></code></pre>
<p>Un autre élément de syntaxe intéressant pour accéder au contenu d'une structure de données est la déstructuration, qui permet de lier des variables à un élément d'une structure plutôt qu'à son ensemble. Par exemple, si l'on désire créer une fonction <code>affiche-point</code> qui prend en paramètre un vecteur contenant l'abscisse et l'ordonnée, plutôt que d'accéder manuellement au premier et au second élément du vecteur, on peut écrire le code suivant :</p>
<pre><code class="clojure"><span class="p">(</span><span class="kd">defn </span> <span class="nv">affiche-point</span> <span class="p">[[</span><span class="nv">x</span> <span class="nv">y</span><span class="p">]]</span>
<span class="p">(</span><span class="nb">println </span><span class="s">"Coordonnées :"</span> <span class="nv">x</span> <span class="s">";"</span> <span class="nv">y</span><span class="p">))</span>
<span class="p">(</span><span class="nf">affiche-point</span> <span class="p">[</span><span class="mi">4</span> <span class="mi">2</span><span class="p">])</span>
<span class="c1">;; affiche "Coordonnées : 4 , 2"</span></code></pre>
<p>Cette déstructuration fonctionne pour les listes et les vecteur, mais également pour les dictionnaires.</p>
<h3 id="interopérabilité-avec-java">Interopérabilité avec Java</h3>
<p>Un atout de Clojure est son interopérabilité avec Java, qui lui permet d'utiliser les multiples bibliothèques existantes. Cela se fait très simplement : <code>(classe. parametres)</code> crée une nouvelle instance de la classe, tandis qu'on accède à une méthode ou à un attribut public en faisant <code>(.methode objet)</code>. Un exemple concret, pour afficher une fenêtre via la biblièthèque Swing :</p>
<pre><code class="clojure"><span class="p">(</span><span class="nb">import </span><span class="o">'</span><span class="p">(</span><span class="nf">javax.swing</span> <span class="nv">JFrame</span> <span class="nv">JLabel</span><span class="p">))</span> <span class="c1">;; importe JFrame et JLabel</span>
<span class="p">(</span><span class="k">def </span><span class="nv">frame</span> <span class="p">(</span><span class="nf">JFrame.</span> <span class="s">"Hello !"</span><span class="p">))</span>
<span class="c1">;; JFrame frame = new JFrame ("Hello !");</span>
<span class="p">(</span><span class="k">def </span><span class="nv">label</span> <span class="p">(</span><span class="nf">JLabel.</span> <span class="s">"Hello, world !"</span><span class="p">))</span>
<span class="c1">;; JLabel label = new JLabel ("Hello, world !");</span>
<span class="p">(</span><span class="nf">.add</span> <span class="p">(</span><span class="nf">.getContentPane</span> <span class="nv">frame</span><span class="p">)</span> <span class="nv">label</span><span class="p">)</span>
<span class="c1">;; frame.getContentPane().add(label)</span>
<span class="p">(</span><span class="nf">.pack</span> <span class="nv">frame</span><span class="p">)</span>
<span class="c1">;; frame.pack ()</span>
<span class="p">(</span><span class="nf">.setVisible</span> <span class="nv">frame</span> <span class="nv">true</span><span class="p">)</span>
<span class="c1">;; frame.setVisible (true)</span></code></pre>
<p>La macro <code>proxy</code> permet également de créer des objets étendant des classes ou implémentant des interfaces. Pour rester dans l'interface graphique, si l'on veut que le programme affiche "Plop !" lorsqu'on clique sur un bouton : </p>
<pre><code class="clojure"><span class="p">(</span><span class="k">def </span><span class="nv">button</span> <span class="p">(</span><span class="nf">javax.swing.JButton.</span> <span class="s">"Plop"</span><span class="p">))</span>
<span class="c1">;; crée le bouton</span>
<span class="p">(</span><span class="nf">.addActionListener</span>
<span class="nv">button</span>
<span class="p">(</span><span class="nb">proxy </span><span class="p">[</span><span class="nv">java.awt.event.ActionListener</span><span class="p">]</span> <span class="p">[]</span>
<span class="p">(</span><span class="nf">actionPerformed</span> <span class="p">[</span><span class="nv">e</span><span class="p">]</span>
<span class="p">(</span><span class="nb">println </span><span class="s">"Plop !"</span><span class="p">))))</span>
<span class="c1">;; on implémente la méthode actionPerformed de l'interface ActionListener</span></code></pre>
<h3 id="pas-vraiment-de-modèle-objet">Pas (vraiment) de modèle objet</h3>
<p>Hormis les mécanismes d’interopérabilité évoqués ci‐dessus, Clojure ne met pas en avant de mécanisme pour faire de la programmation objet au sens strict du terme. Cependant, il existe un certain nombre d’outils permettant d’obtenir sensiblement le même type de fonctionnalités, notamment en termes de polymorphisme.</p>
<p>L’un de ces outils est la notion de protocoles, similaires aux interfaces en programmation objet. Un protocole va en effet définir un certain nombre de fonctions s’appliquant aux éléments d’un type donné. Par exemple, pour définir un protocole contenant une seule fonction, <code>doubler</code> :</p>
<pre><code class="clojure"><span class="p">(</span><span class="kd">defprotocol </span><span class="nv">Doubler</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="p">[</span><span class="nv">this</span><span class="p">]))</span></code></pre>
<p>Il est ensuite possible d'implémenter ce protocole, soit pour de nouveaux types (voir ci-dessous), soit pour des types existants. Par exemple, si on veut implémenter ce protocole pour les nombres et les chaînes de caractères : </p>
<pre><code class="clojure"><span class="p">(</span><span class="nf">extend-protocol</span> <span class="nv">Doubler</span>
<span class="nv">java.lang.Number</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="p">[</span><span class="nv">x</span><span class="p">]</span> <span class="p">(</span><span class="nb">* </span><span class="mi">2</span> <span class="nv">x</span><span class="p">))</span>
<span class="nv">java.lang.String</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="p">[</span><span class="nv">x</span><span class="p">]</span> <span class="p">(</span><span class="nb">str </span><span class="nv">x</span> <span class="nv">x</span><span class="p">)))</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="mi">21</span><span class="p">)</span>
<span class="c1">;; renvoie 42</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="s">"coin"</span><span class="p">)</span>
<span class="c1">;; renvoie "coincoin"</span></code></pre>
<p>En complément des protocoles, Clojure fournit également un outil pour créer de nouveaux types : <code>defrecord</code> (il existe également <code>deftype</code>, un peu plus bas niveau). Cela crée concrètement une classe Java contenant les attributs passés en paramètres, tout en permettant une utilisation similaire à celle des dictionnaires :</p>
<pre><code class="clojure"><span class="p">(</span><span class="kd">defrecord </span><span class="nv">Surface</span> <span class="p">[</span><span class="nv">longueur</span> <span class="nv">largeur</span><span class="p">])</span>
<span class="p">(</span><span class="k">def </span><span class="nv">s</span> <span class="p">(</span><span class="nf">Surface.</span> <span class="mi">2</span> <span class="mi">2</span><span class="p">))</span>
<span class="c1">;; On crée un nouvel objet de la classe Surface</span>
<span class="p">(</span><span class="nf">.longueur</span> <span class="nv">s</span><span class="p">)</span>
<span class="c1">;; On peut accéder aux attributs en utilisant les méthodes d'interopérabilité Java...</span>
<span class="p">(</span><span class="ss">:largeur</span> <span class="mi">2</span><span class="p">)</span>
<span class="c1">;; ... ou comme s'il s'agissait de clés pour un dictionnaire</span></code></pre>
<p>Il est possible, lors de la création d'un nouveau <code>record</code>, d'implémenter directement certaines interfaces :</p>
<pre><code class="clojure"><span class="p">(</span><span class="kd">defrecord </span><span class="nv">Surface</span> <span class="p">[</span><span class="nv">longueur</span> <span class="nv">largeur</span><span class="p">])</span>
<span class="nv">Doubler</span>
<span class="p">(</span><span class="nf">doubler</span> <span class="p">[</span><span class="nv">this</span><span class="p">]</span> <span class="p">(</span><span class="nf">Surface.</span> <span class="p">(</span><span class="nb">* </span><span class="mf">1.41</span> <span class="nv">longueur</span><span class="p">)</span>
<span class="p">(</span><span class="nb">* </span><span class="mf">1.41</span> <span class="nv">largeur</span><span class="p">))))</span>
<span class="p">(</span><span class="k">def </span><span class="nv">s</span> <span class="p">(</span><span class="nf">Surface.</span> <span class="mi">2</span> <span class="mi">2</span><span class="p">))</span>
<span class="p">(</span><span class="ss">:longueur</span> <span class="p">(</span><span class="nf">doubler</span> <span class="nv">s</span><span class="p">))</span>
<span class="c1">;; -> renvoie 2.82</span></code></pre>
<p>Les protocoles permettent donc une forme de polymorphisme assez similaire aux interfaces ou à l'héritage dans la programmation orientée objet. Le choix de la méthode à appeler en fonction du type de l'objet (<em>single dispatch</em>) n'est pas toujours optimal, et il est parfois utile d'avoir un choix de la méthode à appeler qui puisse être arbitraire (<em>multiple dispatch</em>). Pour cela, Clojure fournit également un mécanisme, les <a href="http://clojure.org/multimethods">multiméthodes</a>.</p>
<h2 id="changements-apportés-par-la-version-16">Changements apportés par la version 1.6</h2>
<p>La version 1.6 de Clojure apporte peu de <a href="https://github.com/clojure/clojure/blob/master/changes.md">réelles nouveautés</a>, le langage ayant maintenant acquis une certaine maturité. Un certain nombre de fonctionnalités qui étaient auparavant considérées comme <em>alpha</em> ont été « promues » et sont donc maintenant considérées comme stables, notamment : </p>
<ul>
<li>la création de types via <code>defrecord</code> ou <code>deftype</code> ;</li>
<li>les <a href="http://clojuredocs.org/clojure_core/clojure.core/transient"><em>transients</em></a> (permettant de considérer une variable immuable comme muable tant que c'est au sein de la même fonction) ;</li>
<li>les <a href="http://clojuredocs.org/clojure_core/clojure.core/add-watch"><em>watches</em></a>, permettant d'appeler automatiquement une fonction lorsqu'une variable est modifiée ;</li>
<li>les <a href="http://clojuredocs.org/clojure_core/clojure.core/promise"><em>promises</em></a>, qui permettent de ne pas bloquer le <em>thread</em> à la création de la variable, mais uniquement lorsqu'elle est lue.</li>
</ul><p>Au rang des nouveautés, cette version fournit des interfaces minimales pour permettre l'accès à Clojure depuis d'autres langages tournant sur la machine virtuelle Java. Elle propose également quelques fonctions supplémentaires (<code>some?</code>, <code>if-some</code> et <code>when-some</code>) destinées à simplifier l'écriture d'expressions conditionnelles courantes. Sinon, la majorité des changements concerne des améliorations de mécanismes existants (comme la déstructuration, ou les mécanismes de hachage utilisés par un certain nombre de structures de données), sans compter de nombreuses corrections de bugs. </p>
<p>On notera également que Clojure requiert dorénavant Java 6 (ou supérieur), tandis que la version précédente (mais pas toutes les bibliothèques) pouvait encore tourner sur la version 5 de Java. </p>
<p>Un certain nombre de développements intéressants concernant Clojure ont lieu en dehors du cœur même de Clojure. Il existe notamment un certain nombre de projets pour compiler du code Clojure vers d'autres plate-formes. Parmi ceux-ci, <a href="https://github.com/clojure/clojurescript">ClojureScript</a>, le compilateur Clojure pour le langage javascript, a acquis une certaine stabilité et une relative popularité. Dans un autre registre, le projet <a href="http://typedclojure.org/">Typed Clojure</a> vise à ajouter un typage statique optionnel, à l'instar de ce qui se fait pour d'autres langages typés dynamiquement.</p></div><div><a href="https://linuxfr.org/news/sortie-de-clojure-1-6.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/101477/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/sortie-de-clojure-1-6#comments">ouvrir dans le navigateur</a>
</p>
SegfaultBAudZeroHeureDavy Defaudpalm123patrick_gBruno MichelJiehonghttps://linuxfr.org/nodes/101477/comments.atomtag:linuxfr.org,2005:Diary/344082013-10-15T19:39:24+02:002013-10-15T19:39:24+02:00Valisp, un langage (pseudo-)Lisp au-dessus de ValaLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<a href="#mais-pourquoi">Mais pourquoi ?</a><ul>
<li><a href="#%C3%87a-ressemble-%C3%A0-quoi">Ça ressemble à quoi ?</a></li>
<li><a href="#cest-fait-comment">C'est fait comment ?</a></li>
<li><a href="#%C3%87a-sutilise-comment">Ça s'utilise comment ?</a></li>
<li><a href="#%C3%87a-fait-quoi-en-vrai">Ça fait quoi, en vrai ?</a></li>
<li><a href="#et-%C3%A7a-a-un-int%C3%A9r%C3%AAt">Et ça a un intérêt ?</a></li>
<li><a href="#et-apr%C3%A8s">Et après ?</a></li>
<li><a href="#le-mot-de-la-fin">Le mot de la fin ?</a></li>
</ul>
</li>
</ul><p>Cher journal, je me permets de te présenter un projet personnel à l'intérêt assez limité : le langage jouet <a href="https://github.com/lady-segfault/valisp">Valisp</a>, qui a pour objectif d'ajouter une couche « Lispienne » au langage Vala. </p>
<h2 id="mais-pourquoi">Mais pourquoi ?</h2>
<p>Parce que ! Vala tout seul, ce n'est pas assez rigolo : c'est juste un langage (Vala) qu'il faut compiler dans un autre langage (C) qu'il faut ensuite compiler à nouveau, ce qui est beaucoup trop direct, admettons-le. </p>
<p><img src="//img.linuxfr.org/img/687474703a2f2f696d61676573332e77696b69612e6e6f636f6f6b69652e6e65742f5f5f636232303133303132333230303732352f676c65652f696d616765732f362f36662f57652d6e6565642d746f2d676f2d6465657065725f696e63657074696f6e2e6a7067/We-need-to-go-deeper_inception.jpg" alt="We need to go deeper" title="Source : http://images3.wikia.nocookie.net/__cb20130123200725/glee/images/6/6f/We-need-to-go-deeper_inception.jpg"></p>
<p>Valisp est donc un langage qu'il faut compiler dans un autre langage qu'il faut compiler dans un autre langage qu'il faut compiler à nouveau. Et en plus, comme c'est inspiré du Lisp, il y a plein de parenthèses.</p>
<h3 id="Ça-ressemble-à-quoi">Ça ressemble à quoi ?</h3>
<p>À du Lisp (c'est le but), mais avec quelques informations de types en plus (parce que Vala est typé statiquement et que le but est de pouvoir utiliser Vala directement). Un petit exemple de code qui utilise la bibliothèque Gtk+ (c'est l'adaptation du premier programme de la page des <a href="https://wiki.gnome.org/Vala/GTKSample">exemples d'utilisation de Gtk+ en Vala</a> :</p>
<pre><code class="lisp"><span class="p">(</span><span class="nv">defn</span> <span class="nv">main</span> <span class="nv">int</span> <span class="nv">[args</span> <span class="nv"><string>]</span>
<span class="p">(</span><span class="nv">Gtk.init</span> <span class="p">(</span><span class="nv">ref</span> <span class="nv">args</span><span class="p">))</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">[[win</span> <span class="p">(</span><span class="nv">new</span> <span class="nv">Gtk.Window</span><span class="p">)</span><span class="nv">]</span>
<span class="nv">[button</span> <span class="p">(</span><span class="nv">new</span> <span class="nv">Gtk.Button.with_label</span> <span class="s">"Plop!"</span><span class="p">)</span><span class="nv">]]</span>
<span class="p">(</span><span class="nv">win.destroy.connect</span> <span class="nv">Gtk.main_quit</span><span class="p">)</span>
<span class="p">(</span><span class="nv">set!</span> <span class="nv">win.title</span> <span class="s">"42"</span><span class="p">)</span>
<span class="p">(</span><span class="nv">button.clicked.connect</span>
<span class="p">(</span><span class="nv">fn</span> <span class="nv">void</span> <span class="nv">[]</span>
<span class="p">(</span><span class="nv">set!</span> <span class="nv">button.label</span> <span class="s">"Coin!"</span><span class="p">)))</span>
<span class="p">(</span><span class="nv">win.add</span> <span class="nv">button</span><span class="p">)</span>
<span class="p">(</span><span class="nv">win.show_all</span><span class="p">)</span>
<span class="p">(</span><span class="nv">Gtk.main</span><span class="p">)</span>
<span class="mi">0</span><span class="p">))</span></code></pre>
<p>(Une fois compilé (et re-compilé avec <code>valac</code>), ça affiche une fenêtre avec un bouton « Plop! » qui devient « Coin! » quand on clique dessus.) </p>
<p>Donc voilà, il n'y a rien de très original si vous avez déjà utilisé des dialectes de Lisp, mis à part le <code>int</code> et le <code><string></code> dans la définition de la fonction main, le premier spécifiant un type de retour <code>int</code> et le second un paramètre de type <code>tableau de string</code>.</p>
<h3 id="cest-fait-comment">C'est fait comment ?</h3>
<p>Comme la syntaxe est proche du Lisp, le plus simple était d'écrire le compilateur dans un langage de ce type, puisque grâce à l'homoiconicité (j'espère que j'utilise bien le bon terme) c'est trivial de prendre un fichier source et d'obtenir une structure de données correspondant au code. Comme j'aime bien Clojure j'ai donc utilisé ce langage (et parce que comme c'est compilé sur la JVM et qu'il faut charger toutes les classes de Clojure avant que le programme s'exécute, ça met quelques secondes et ça fait croire que le compilateur fait des choses intelligentes et compliquées alors qu'en fait pas du tout). </p>
<h3 id="Ça-sutilise-comment">Ça s'utilise comment ?</h3>
<p>Il y a deux possibilités : soit installer <a href="http://leiningen.org/">Leiningen</a>, cloner le <a href="https://github.com/lady-segfault/valisp">repository git</a> et faire </p>
<pre><code>$ lein run
</code></pre>
<p>dans le bon répertoire ; ou alors, plus simple, récupérer <a href="http://segfault.ouvaton.org/public/valisp/valisp-0.1-standalone.jar">le Jar de la version 0.1</a> et le lancer avec :</p>
<pre><code>$ java -jar valisp-0.1-standalone.jar
</code></pre>
<p>Dans tous les cas, ce sera plus utile si vous passez en paramètre un fichier qui contient du code Valisp. Il y a quelques <a href="https://github.com/lady-segfault/valisp/tree/master/doc/examples">exemples</a> de programmes qui devraient compiler sur Github. </p>
<h3 id="Ça-fait-quoi-en-vrai">Ça fait quoi, en vrai ?</h3>
<p>Bon, en vrai c'est super pompeux d'appeler ça un compilateur. Déjà, tout ce qui concerne la transformation du code Valisp en <em>Abstract Syntax Tree</em>, c'est Clojure qui le fait grâce à un appel à <code>read</code>, donc c'est vite fait. Il n'y a donc que l'aspect « génération de code » que j'ai écrite. En plus, je suis tellement une feignasse qu'il n'y a aucune réelle vérification que le code produit est correct (après tout, il y a déjà le compilateur Vala pour ça, j'allais pas m'embêter (et en plus, comme le but c'est de pouvoir utiliser directement les fonctions et objets qui existent dans Vala, ça aurait nécessité un boulot assez compliqué, notamment de d'abord <em>parser</em> les<br>
API Vala)). Le tout est sans doute codé de façon assez <em>gruikesque</em>, mais ça marchouille avec quelques exemples.</p>
<p>Sinon, il y a un certain nombre de choses qui sont implémentées, comme la définition de fonctions nommées, de closures, les définitions de variable, les boucles, etc (la liste des fonctionnalités implémentées est <a href="https://github.com/lady-segfault/valisp/blob/master/doc/features.md">ici</a>). Il y a aussi des choses qui ne le sont pas : pas de possibilité de définir de nouvelle classe, par exemple, ni de définir une fonction <em>template</em>. </p>
<h3 id="et-ça-a-un-intérêt">Et ça a un intérêt ?</h3>
<p>Honnêtement, non, le but c'était surtout de m'amuser un peu avec Clojure. Après, dans l'absolu, on aime ou on n'aime pas la syntaxe Lisp, mais je trouve élégante l'uniformité de ce langage, au lieu d'avoir souvent des structures assez différentes pour faire la même chose dans des contextes différent. Deux exemples pour Vala (et pas mal d'autres langages) :</p>
<ul>
<li>la définition de fonctions et de closures qui ont une syntaxe assez différentes (<code>int nom (int x) {return 42 * x;}</code> VS <code>(x) => {return 42 * x}</code> ;</li>
<li>la différence pour la condition selon qu'on veuille pouvoir utiliser la valeur comme résultat ou pas (<code>if (condition) {expr1} else {expr2}</code> VS <code>condition?expr1:expr2</code>.</li>
</ul><p>Cela dit, pour faire des applications GNOME en Lisp, je recommande plutôt d'utiliser Clojure avec les bindings java-gnome, ou d'autres bindings pour d'autres Lisp.</p>
<h3 id="et-après">Et après ?</h3>
<p>Le but étant surtout de faire un langage jouet, et le projet étant assez inutile tout de même, je ne pense pas mettre énormément d'énergie pour développer ce projet — il y a déjà suffisamment de langages obscurs comme ça. Éventuellement, si j'ai la motivation, j'essaierais bien de porter le « compilateur » Valisp en Valisp, parce que ce serait cool, ce qui implique quand même un peu de boulot (notamment de créer (ou de voir si ça existe dans les libs Vala) une structure de données pour stocker les listes façon Lisp).</p>
<h3 id="le-mot-de-la-fin">Le mot de la fin ?</h3>
<p>Valisp ça sert à rien, mais Clojure c'est cool (même si c'est lent au démarrage). </p><div><a href="https://linuxfr.org/users/elly/journaux/valisp-un-langage-pseudo-lisp-au-dessus-de-vala.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/99990/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/elly/journaux/valisp-un-langage-pseudo-lisp-au-dessus-de-vala#comments">ouvrir dans le navigateur</a>
</p>
Lizzie Crowdaggerhttps://linuxfr.org/nodes/99990/comments.atomtag:linuxfr.org,2005:Diary/328332012-07-15T05:26:08+02:002012-07-15T13:11:07+02:00Tiny 'Nux Tarot, version 0.2Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Cher journal, </p>
<p>J'ai le plaisir de t'annoncer la sortie de la version 0.2 de <a href="https://github.com/lady-segfault/tnt">Tiny 'Nux Tarot</a>, qui est aussi sa première version publique. J'espère par conséquent avoir droit à un peu d'indulgence si le code source est immonde /o\</p>
<h3 id="toc_0">Qu'est-ce que c'est ?</h3>
<p>Comme son nom l'indique, Tiny 'Nux Tarot (ou TnT) est un bébé jeu de tarot. Il est écrit dans le langage <a href="https://live.gnome.org/Vala">Vala</a> et utilise la bibliothèque Gtk+, ainsi que les images de cartes à jouer du projet <a href="http://nongnu.org/cardpics/cardpics.fr.html">Cardpics</a>. Il est, c'est assez peu original, distribué sous licence GNU GPL.</p>
<h3 id="toc_1">Fonctionnalités</h3>
<p>Comme son nom l'indique aussi, ainsi que son numéro de version, TnT a des fonctionnalités assez limitées, mais il devrait néanmoins permettre de faire des parties de tarot à quatre joueurs, sans avoir besoin d'avoir trois autres joueurs de tarot sous la main. </p>
<p>Plus en détail :</p>
<ul><li>les règles de base du tarot sont implémentées, bien que certains cas particuliers ne soient pas gérés (notamment : les poignées, les garde sans et garde contre, le petit au bout ou encore l'excuse au dernier tour) ;</li>
<li>seul un jeu à quatre joueurs est possible (mais c'est tout de même le plus intéressant) ;</li>
<li>l'IA existe, bien qu'assez rudimentaire : elle essaie de se défendre comme elle peut et peut même prendre lorsqu'elle a un bon jeu, mais elle est plutôt simpliste ;</li>
<li>l'interface graphique permet de jouer quelques parties mais est spartiate (même si la personne qui prend est indiquée par des têtes de mort à côté de son nom, et que je ne suis pas peu fière de cette idée) ;</li>
<li>il n'y a pas encore d'internationalisation, et le jeu est un peu en franglais à cause du fait qu'il est difficile de trouver des traductions anglaises pour les termes propres au tarot ;</li>
<li>pour finir, il est possible de jouer à deux êtres humains sur la même machine, même si cela a assez peu d'intérêt — le jeu en réseau n'est pas (encore ?) supporté.</li>
</ul><h3 id="toc_2">Téléchargement / installation</h3>
<p>Là non plus, rien de très original : la <a href="https://github.com/downloads/lady-segfault/tnt/tnt-0.2.tar.bz2">tarball de la version 0.2</a> est téléchargeable sur Github, après quoi un classique «./configure, make, make install» devrait faire l'affaire, à condition d'avoir les bibliothèques requises (Gtk+3.x, libgee et libvala — dans ce cas, le compilateur Vala n'est pas nécessaire, vu que le code est déjà traduit en C). En tout cas en théorie : je n'ai pas eu l'occasion de le tester sur d'autres machines que les miennes, donc je serais assez curieuse d'avoir des retours à ce sujet.</p>
<p>Il est aussi possible d'utiliser git pour récupérer la dernière version (même si à l'heure où j'écris ces lignes, le code devrait être identique à la tarball).</p>
<h3 id="toc_3">Utilisation</h3>
<p>Pour lancer TnT, il suffit de l'invoquer par la ligne de commande. Il n'y a pas besoin d'options, à moins que vous vouliez jouer à 2 sur le même écran (bon en vrai, moi je m'en sers pour débugger, mais ça me parait très peu pratique pour jouer) : </p>
<pre>
<code class="">$ tnt 2
</code>
</pre>
<p>…ou que vous n'ayez pas envie de vous appeler «Player 1» et de jouer avec «Player 2, 3, 4», mais «Plop» et de jouer avec «Miaou, Coin, Pan» :</p>
<pre>
<code class="">$ tnt 1 Plop Miaou Coin Pan
</code>
</pre>
<p>Le jeu en lui même est plutôt basique et ne nécessite pas, je pense, de manuel particulier, vu que ça se limite à cliquer sur une carte pour jouer la carte (ou la destiner au chien), sur «OK» pour passer au tour suivant, ou sur «Passe/Petite/Garde» pour «Passer/Prendre/Garder». Sinon, le nom de la personne (ou de l'IA) qui a pris est indiqué en rouge, tandis que celle qui commence le tour se voit affubler du bleu (oui, bon, ça pourrait être plus joli, mais ça aurait aussi pu être jaune). Ah, et la personne qui a pris est indiquée par des têtes de mort, aussi, je vous l'ai dit ? </p>
<p>Voilà, et pour finir, <a href="http://segfault.ouvaton.org/index.php?post/2012/07/14/Some-Tiny-Nux-Tarot-screenshots">quelques screenshots</a>.</p>
<h3 id="toc_4">Liens</h3>
<ul><li>Page github du projet : <a href="https://github.com/lady-segfault/tnt">https://github.com/lady-segfault/tnt</a></li>
<li>Tarball : <a href="https://github.com/downloads/lady-segfault/tnt/tnt-0.2.tar.bz2">https://github.com/downloads/lady-segfault/tnt/tnt-0.2.tar.bz2</a></li>
<li>Cardpics : <a href="http://nongnu.org/cardpics/cardpics.fr.html">http://nongnu.org/cardpics/cardpics.fr.html</a></li>
</ul><div><a href="https://linuxfr.org/users/elly/journaux/tiny-nux-tarot-version-0-2.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/94837/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/elly/journaux/tiny-nux-tarot-version-0-2#comments">ouvrir dans le navigateur</a>
</p>
Lizzie Crowdaggerhttps://linuxfr.org/nodes/94837/comments.atomtag:linuxfr.org,2005:Diary/302492010-09-29T05:57:41+02:002010-09-29T05:57:41+02:00Roman de fantasy sous Licence Art Libre
Bonjour cher journal, et pardonne moi pour ce qui pourrait ressembler à une pub déguisée...<br />
<br />
Mais voilà, je souhaitais annoncer la publication (en auto-édition) de mon roman de Fantasy <em>Pas tout à fait des hommes</em>, disponible en version papier (depuis peu) et en téléchargement (depuis plus longtemps), le tout sous Licence Art Libre.<br />
<br />
De quoi ça parle ? <br />
<br />
<code>Kalia, la seule elfe de la ville à travailler dans la garde, se contente d'ordinaire d'essayer de survivre et d'éviter les ennuis...<br />
<br />
Du moins, jusqu'au jour où elle rencontre Axelle, une voleuse démoniaque qui va bouleverser sa vie. <br />
<br />
Avant de réaliser ce qui lui arrive, Kalia va se retrouver confrontée à des orcs révolutionnaires, des nains remontés, un général belliqueux, un vampire schizophrène, une prophétie obscure, une épée sacrée, un Élu au coeur pur, ainsi qu'une multitude d'autres choses potentiellement mortelles mais au nom moins impressionnant.</code><br />
<br />
D'un point de vue technique, ce n'est certes pas du hacking de haut niveau, mais ce roman a été réalisé avec des technologies libres, c'est-à-dire LaTeX pour le document en lui-même et Inkscape pour la couverture de la version papier. Du coup j'avais envie de faire partager ça à la «communauté», parce que si j'attendais de publier un vrai logiciel libre fonctionnel, ben les moules auraient des dents. <br />
<br />
Concernant le LaTeX, le cœur du code réside dans trois petits fichiers (un style et deux classes), qui doivent faire en tout et pour tout quelques centaines de ligne. (Évidemment, pour un roman ou une nouvelle c'est principalement du texte brut et il y a peu de «code» LaTeX à proprement parler, mais il y a quand même quelques spécificités à gérer.)<br />
<br />
Je n'ai pas la prétention que ce soit génial et super bien écrit (le roman lui-même non plus, d'ailleurs ;)), mais je me dis que ça peut éventuellement intéresser des écrivains qui voudraient utiliser du LaTeX pour des nouvelles ou romans. À vrai dire je n'ai pas trouvé beaucoup d'exemples pour ce genre d'utilisations et si d'autres personnes ont fait autrement je serais curieuse de voir à quoi ça ressemble.<br />
<br />
<br />
<br />
<br />
Liens : <br />
<ul><li>La page du roman sur le site d'ILV-Édition (pour commander la version papier ou télécharger le PDF) : <a href="http://www.ilv-edition.com/librairie/pas-tout-fait-des-hommes.html">http://www.ilv-edition.com/librairie/pas-tout-fait-des-homme(...)</a></li>
<li>Mon site, où l'on peut trouver ce roman ainsi que d'autres textes : <a href="http://reveries.info">http://reveries.info</a></li>
<li>Les sources LaTeX : <a href="http://reveries.info/dl/endr/endr-1.0.tar.gz">http://reveries.info/dl/endr/endr-1.0.tar.gz</a></li>
<li>Un peu plus d'explications sur les classes définies : <a href="http://reveries.info/dotclear/admin/post.php?id=16&upd=1">http://reveries.info/dotclear/admin/post.php?id=16&upd=1</a></li>
</ul><div><a href="https://linuxfr.org/users/elly/journaux/roman-de-fantasy-sous-licence-art-libre.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/56524/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/elly/journaux/roman-de-fantasy-sous-licence-art-libre#comments">ouvrir dans le navigateur</a>
</p>
Lizzie Crowdaggerhttps://linuxfr.org/nodes/56524/comments.atom