URL: https://linuxfr.org/news/sortie-de-clojure-1-6 Title: Sortie de Clojure 1.6 Authors: Lizzie Crowdagger BAud, Davy Defaud, ZeroHeure, palm123, patrick_g, Bruno Michel et Jiehong Date: 2014年03月09日T10:03:57+01:00 License: CC By-SA Tags: clojure et lisp Score: 31 Le 25 mars, Clojure est sorti en version 1.6, l'occasion de se pencher un peu sur ce langage. 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 *Common Language Runtime* de .NET. ---- [Site officiel](http://clojure.org/) [Notes de version](https://github.com/clojure/clojure/blob/master/changes.md) [Clojure-doc (site de documentation, contenant notamment des tutoriaux)](http://clojure-doc.org/) ---- # Présentation du langage # 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. # Installation # La manière la plus simple d'utiliser Clojure est d'utiliser [Leiningen](http://leiningen.org/). 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. Cet outil permet également (via `lein repl`) de lancer un REPL (_Read-Eval-Print-Loop_) qui permet de manipuler interactivement du code. Cf. le tutorial [Clojurer des regexps avec Java, en Lisp :)](http://linuxfr.org/users/philippemc/journaux/clojurer-des-regexps-avec-java-en-lisp) qui présente un peu plus cet aspect-là. Pour tester Clojure sans avoir à installer quoi que ce soit sur sa machine, il est également possible d'utiliser le site [Try Clojure](http://tryclj.com/), qui fournit un REPL accessible par le web. # Un peu de syntaxe # La syntaxe de Clojure ne dépaysera pas les habitués de Lisp, ou de ses variantes. ```clojure (println "Hello, world !") ;; affiche "Hello, world !" (+ 2 2) ;; renvoie 4 (* 3 2) ;; renvoie 6 ``` Clojure est [dynamiquement typé](http://fr.wikipedia.org/wiki/Typage_dynamique) : 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 `type` : ```clojure (type 2) ;; renvoie java.lang.Long (type "Hello, world !") ;; renvoie java.lang.String ``` 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, `println`, dans les suivants `+` et `*` (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. Pour créer une liste, il suffit de demander à ce qu'elle ne soit pas évaluée, _via_ `quote`, ou le caractère spécial `'` : ```clojure (quote (1 2 3 4 5)) ;; renvoie (1 2 3 4 5) '(1 2 3 4 5) ;; identique ``` C'est le principe fondateur de Lisp (le nom _Lisp_ vient de _LISt Processing_) : 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 : * `(cons x une-liste)` ajoute l'élément `x` au début de la liste `une-liste` ; * `(first une-liste)` retourne le premier élément d'`une-liste` (correspond à `car` dans Lisp) ; * `(rest une-liste)` retourne une liste comprenant tous les éléments d'`une-liste`, sauf le premier (correspond à `cdr` dans Lisp). Quelques exemples : ```clojure (def une-liste '(1 2 3 4 5)) ;; définit une variable, une-liste, contenant (1 2 3 4 5) (first une-liste) ;; renvoie 1 (rest une-liste) ;; renvoie (2 3 4 5) (cons 0 une-liste) ;; renvoie (0 1 2 3 4 5) ``` # Particularités de Clojure # ## Structures de données ## En Lisp, la liste est donc _la_ 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 : ```clojure (defn doubler [x] (* 2 x)) (doubler 2) ;; renvoie 4 ``` Les crochets autour des paramètres de la fonction (en l'occurrence, `x`) 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 : ```clojure [1 2 3 4] ``` À 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 : * 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 ; * à 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. Clojure fournit également une syntaxe pour les ensembles : ```clojure #{"bleu" "rouge" "vert"} ``` ainsi que pour les dictionnaires : ```clojure {1 "bleu" 2 "rouge" 3 "vert"} ``` ## Un accent sur l'immuabilité ## À 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 : ```clojure (vector 1 2 3 4) ;; crée un vecteur (set '("bleu" "rouge" "vert")) ;; crée un ensemble (array-map 1 "bleu" 2 "rouge" 3 "vert") ;; crée un dictionnaire ``` 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 [persistantes](http://fr.wikipedia.org/wiki/Structure_de_donn%C3%A9es_persistante). 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 `conj` : ```clojure (def mon-vecteur [1 2 3 4]) (conj mon-vecteur 5) ;; renvoie [1 2 3 4 5] mon-vecteur ;; contient toujours [1 2 3 4] ``` 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 _thread_ modifie les données sur lesquelles on est en train de travailler. 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é `def` n’entraînera un changement qui ne sera visible qu’à l’intérieur du même _thread_, il existe également `atom`, `agent` et `ref` qui permettent de partager des données entre plusieurs _threads_, avec des mécanismes un peu différents : * `atom` 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 ; * `ref` permet une modification coordonnée de plusieurs variables, de manière synchrone ; * `agent` permet une modification coordonnée de plusieurs variables, de manière asynchrone. ## Du sucre syntaxique ## 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é : ```clojure (def v [1 2 3 4]) (def d {1 "bleu" 2 "rouge" 3 "vert"}) (v 2) ;; renvoie 3 (d 1) ;; renvoie "bleu" ``` En ce qui concerne les dictionnaires, leurs clés correspondent souvent à des mots-clés, des identifiants commençant par le caractère `:`. 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 : ```clojure (def point {:x 3 :y 4}) (:x point) ;; renvoie 3 ``` 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 `affiche-point` 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 : ```clojure (defn affiche-point [[x y]] (println "Coordonnées :" x ";" y)) (affiche-point [4 2]) ;; affiche "Coordonnées : 4 , 2" ``` Cette déstructuration fonctionne pour les listes et les vecteur, mais également pour les dictionnaires. ## Interopérabilité avec Java ## 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 : `(classe. parametres)` crée une nouvelle instance de la classe, tandis qu'on accède à une méthode ou à un attribut public en faisant `(.methode objet)`. Un exemple concret, pour afficher une fenêtre via la biblièthèque Swing : ```clojure (import '(javax.swing JFrame JLabel)) ;; importe JFrame et JLabel (def frame (JFrame. "Hello !")) ;; JFrame frame = new JFrame ("Hello !"); (def label (JLabel. "Hello, world !")) ;; JLabel label = new JLabel ("Hello, world !"); (.add (.getContentPane frame) label) ;; frame.getContentPane().add(label) (.pack frame) ;; frame.pack () (.setVisible frame true) ;; frame.setVisible (true) ``` La macro `proxy` 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 : ```clojure (def button (javax.swing.JButton. "Plop")) ;; crée le bouton (.addActionListener button (proxy [java.awt.event.ActionListener] [] (actionPerformed [e] (println "Plop !")))) ;; on implémente la méthode actionPerformed de l'interface ActionListener ``` ## Pas (vraiment) de modèle objet ## 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. 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, `doubler` : ```clojure (defprotocol Doubler (doubler [this])) ``` 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 : ```clojure (extend-protocol Doubler java.lang.Number (doubler [x] (* 2 x)) java.lang.String (doubler [x] (str x x))) (doubler 21) ;; renvoie 42 (doubler "coin") ;; renvoie "coincoin" ``` En complément des protocoles, Clojure fournit également un outil pour créer de nouveaux types : `defrecord` (il existe également `deftype`, 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 : ```clojure (defrecord Surface [longueur largeur]) (def s (Surface. 2 2)) ;; On crée un nouvel objet de la classe Surface (.longueur s) ;; On peut accéder aux attributs en utilisant les méthodes d'interopérabilité Java... (:largeur 2) ;; ... ou comme s'il s'agissait de clés pour un dictionnaire ``` Il est possible, lors de la création d'un nouveau `record`, d'implémenter directement certaines interfaces : ```clojure (defrecord Surface [longueur largeur]) Doubler (doubler [this] (Surface. (* 1.41 longueur) (* 1.41 largeur)))) (def s (Surface. 2 2)) (:longueur (doubler s)) ;; -> renvoie 2.82 ``` 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 (_single dispatch_) n'est pas toujours optimal, et il est parfois utile d'avoir un choix de la méthode à appeler qui puisse être arbitraire (_multiple dispatch_). Pour cela, Clojure fournit également un mécanisme, les [multiméthodes](http://clojure.org/multimethods). # Changements apportés par la version 1.6 # La version 1.6 de Clojure apporte peu de [réelles nouveautés](https://github.com/clojure/clojure/blob/master/changes.md), le langage ayant maintenant acquis une certaine maturité. Un certain nombre de fonctionnalités qui étaient auparavant considérées comme _alpha_ ont été « promues » et sont donc maintenant considérées comme stables, notamment : * la création de types via `defrecord` ou `deftype` ; * les [_transients_](http://clojuredocs.org/clojure_core/clojure.core/transient) (permettant de considérer une variable immuable comme muable tant que c'est au sein de la même fonction) ; * les [_watches_](http://clojuredocs.org/clojure_core/clojure.core/add-watch), permettant d'appeler automatiquement une fonction lorsqu'une variable est modifiée ; * les [_promises_](http://clojuredocs.org/clojure_core/clojure.core/promise), qui permettent de ne pas bloquer le _thread_ à la création de la variable, mais uniquement lorsqu'elle est lue. 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 (`some?`, `if-some` et `when-some`) 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. 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. 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, [ClojureScript](https://github.com/clojure/clojurescript), le compilateur Clojure pour le langage javascript, a acquis une certaine stabilité et une relative popularité. Dans un autre registre, le projet [Typed Clojure](http://typedclojure.org/) vise à ajouter un typage statique optionnel, à l'instar de ce qui se fait pour d'autres langages typés dynamiquement.

AltStyle によって変換されたページ (->オリジナル) /