Serveur d'impression

Clojure: une alternative mature à Java – Bien choisir son serveur d impression

Le 12 mars 2020 - 15 minutes de lecture

Cette année, nous célébrons le 25e anniversaire de Java, qui est actuellement le langage de programmation le plus populaire. Autrement dit, selon l'indice TIOBE. Devenir le langage de programmation le plus populaire est un exploit difficile et sûrement au début de Java, beaucoup de gens devaient être convaincus.

C'est ainsi que Java était positionné comme une meilleure alternative à la langue de son choix à l'époque:

«Nous recherchions les programmeurs C ++. Nous avons réussi à en tirer beaucoup à mi-chemin de Lisp. »

– Guy Steele, co-auteur de la spécification du langage Java

Et c'est ainsi que de nombreux programmeurs sont passés à Java, se déplaçant à mi-chemin sur le chemin de l'illumination. Cet article concerne la partie suivante, le passage à Lisp… Ou en tout cas: une lisp, le summum de la productivité, selon Steele.

Le lisp dont nous parlons est Clojure, un langage de programmation pour la JVM qui a été introduit en 2007 par Rich Hickey. Lors de l'édition 2014 de Java One, Hickey a tenu une conférence. Il a commencé avec la citation suivante:

«Beaucoup des meilleurs programmeurs et des programmeurs les plus productifs que je connaisse écrivent tout dans Clojure et ne jurent que par cela, puis produisent simplement des choses ridiculement sophistiquées en très peu de temps. Et c'est la productivité du programmeur qui compte. »

– Adrian Cockroft, (ancien) Netflix

Le point fort de Rich était que Clojure vous permet d'écrire des programmes meilleurs et plus flexibles, et surtout vous rend beaucoup plus productif que l'utilisation de Java.

Cela pourrait certainement être la raison pour laquelle une grande entreprise comme Netflix a adopté Clojure comme une alternative plus mature à Java. Un autre exemple est WalmartLabs: définir l'image sélectionnée

"Clojure réduit notre base de code à environ un cinquième de la taille qu'elle aurait si nous avions écrit en Java"

– Anthony Marcar, architecte, WalmartLabs

Désormais, le langage a fait ses preuves dans l'industrie. Peut-être une bonne raison pour vous de passer à Clojure? Nous avons énuméré dix avantages d'utiliser Clojure pour vous.

VOIR AUSSI: Comment programmer en toute sécurité en Java en 2020

1. Clojure est simple

Rich Hickey a consacré une année entière de sa vie à la conception de Clojure. Il a décidé que le langage devait être simple. Non pas que ce soit un facile Langue. Certes, si Clojure était facile à apprendre, il pourrait initialement séduire certains programmeurs Java, mais ce n'est pas le cas. En effet, le chemin vers l'illumination du programmeur n'est jamais facile. Peut-être même douloureux, avec toutes ces parens, et cette notation de préfixe idiote.

Cependant, une fois que vous avez atteint Nirvana, vous constaterez que la syntaxe de Clojure est en fait très simple. Un exemple de sa simplicité est l'écriture de fonctions. Clojure vous encourage à écrire des fonctions pures – c'est-à-dire des fonctions qui ne produisent aucun effet secondaire. Rich Hickey a reconnu que la caractéristique de immutabilité aidera à atteindre cet objectif. Parce que si vos variables sont garanties immuables, votre code sera mieux raisonné et beaucoup plus facile à tester.

2. Clojure est orienté données

La simplicité vient également du fait que Clojure n'est pas orienté objet, il est orienté données. Les données sont représentées par des cartes de hachage immuables au lieu de classes, comme en Java. Ces structures de données sont faciles à manipuler avec des fonctions, mais elles sont mutuellement indépendantes. Par conséquent, Clojure est philosophiquement aligné avec la citation suivante d'Alan J. Perlis:

«Il vaut mieux que 100 fonctions fonctionnent sur une seule structure de données que 10 fonctions fonctionnent sur 10 structures de données.»

L'orientation des objets vous oblige à réinventer continuellement la roue, car elle vous oblige à mettre vos données en classes. Au lieu de cela, avec Clojure, vous pouvez utiliser le même ensemble limité de structures de données pour tous vos cas d'utilisation. De plus, chaque nouvelle classe en OO est un langage en soi: vous devez d'abord apprendre ses méthodes afin d'accéder aux structures de données qui se cachent derrière. De plus, comme les cartes dans Clojure sont immuables, vous n'avez pas à vous soucier des effets secondaires.

Voici un petit exemple. Dans Ring, une bibliothèque pour écrire des applications Web, un gestionnaire n'est rien d'autre qu'une fonction qui attend une table de hachage (la demande) et renvoie une table de hachage (la réponse):



(defn what-is-my-ip [request]
  : statut 200
   : en-têtes "Content-Type" "text / plain"
   : corps (: demande d'add-remote))

L'espace de noms, qui est la portée de ce gestionnaire, ne dépend pas de la bibliothèque Ring. La seule chose que nous devons savoir, c'est à quoi devrait ressembler une demande dans Ring, c'est-à-dire à quelles clés nous pouvons nous attendre dans la table de hachage, et quelles clés nous devons retourner afin de produire une réponse valide. Tout ce qui se passe entre la requête et la réponse n'est rien d'autre que des calculs avec des hash-maps normales. C'est sûrement beaucoup plus simple que de travailler avec HttpServletRequest et HttpServletResponse.

3. Clojure nécessite moins de lignes de code

Lorsque vous utilisez Clojure, vous n'avez pas à supporter le fardeau de l'immense surcharge qui se produit avec les langages orientés objet. Cette surcharge consiste à écrire l'interface et le code d'implémentation pour «protéger» vos structures de données. Cela signifie que vous avez besoin de moins de lignes de code Clojure que de lignes de code Java pour obtenir le même résultat.

Une autre raison pour laquelle vous devez écrire moins de lignes de code est que Clojure est un fonctionnel langage de programmation. Ces langages sont connus pour être plus expressifs que les langages de programmation impératifs. Par exemple, dans Clojure, vous n'avez pas à écrire de boucles ou d'itérations pour les calculs sur les collections. Tout ce que vous avez à faire est de postuler ordre supérieur fonctions, telles que cartographier, filtrer et réduire à vos collections.

Bien sûr, à partir de la version 8, le langage Java incorporait déjà des fonctionnalités telles que lambda et des fonctions d'ordre supérieur via l'API Streams. Mais il y a plus. En raison du fait que Clojure est un lisp, vous pouvez empêcher l'écriture de code passe-partout en utilisant macro. Ce sont des fonctions qui sont exécutées au moment de la compilation et qui acceptent des expressions comme arguments. Les expressions sont représentées sous forme de données, que vous pouvez également transformer à l'aide des fonctions Clojure. Tout cela revient à générer du code, vous n'avez donc pas à écrire le code passe-partout vous-même.

Le site Web TodoBackend contient plusieurs implémentations différentes de l'API Todo-list. Vous pouvez également l'utiliser pour comparer des langues. En comparant l'implémentation de Clojure avec Java 7 et 8, nous voyons ce qui suit:

  • Clojure: 168 lignes de Clojure (y compris la configuration de construction)
  • Java 7 + Spring MVC: 555 lignes de XML, 228 lignes de code Java, 56 lignes de Groovy
  • Java 8 + Spring 4 Boot: 200 lignes de Java, 37 lignes de Groovy

Ici, nous n'avons pas inclus les lignes de code des bibliothèques et des frameworks utilisés. Si nous le faisions, nous nous attendons à de meilleurs résultats pour Clojure.

Les avantages de moins de lignes de code sont évidents: un programme plus petit contient moins de bogues, est plus facile à raisonner et – en raison de sa simplicité – est plus robuste.

4. Clojure prend en charge plusieurs plates-formes

L'écosystème Clojure vous permet de réutiliser vos connaissances de la langue dans plusieurs environnements. Pour le développement côté serveur, vous utilisez Clojure sur la JVM (ou dans .NET via ClojureCLR).

Pour le développement frontal, vous utilisez ClojureScript, qui se compile en JavaScript. Le compilateur ClojureScript peut être utilisé pour les programmes qui s'exécutent dans le navigateur, mais aussi côté serveur – en utilisant NodeJS.

Beaucoup d'efforts ont été déployés pour assurer la cohérence des implémentations de Clojure. Ils divergent uniquement en raison des limites de la plate-forme. Un exemple d'une telle limitation est l'absence de multi-threading dans le navigateur.

De nombreuses bibliothèques Clojure prennent en charge Clojure et ClojureScript. Vous pouvez donc réutiliser vos connaissances sur les deux plateformes. Les développeurs de bibliothèques peuvent utiliser conditionnelles du lecteur, qui offrent la possibilité d'utiliser le même fichier de code pour tous les environnements cibles.

Les développeurs d'applications peuvent également utiliser les conditions du lecteur pour écrire un morceau de code pour toutes les plateformes. Par exemple, le code suivant contient une variable avec une valeur NaN. Sur la plate-forme JVM («clj»), il est défini comme Double / NaN, en JavaScript («cljs») comme NaN à partir de l'espace de noms global.



(def pas un nombre
  #? (: clj Double / NaN
     : cljs js / NaN
     : zéro par défaut))

5. Clojure est interactif

Clojure est un langage de programmation interactif. Grâce à la boucle REPL (lecture-évaluation-impression), vous recevez un retour immédiat. Pendant le développement, vous pouvez redéfinir vos fonctions et les tester à nouveau, même lorsque l'application est en cours d'exécution – sans démarrer un nouveau cycle de compilation. Des outils tels que JRebel ou Quarkus pour le rechargement à chaud du code ne sont pas nécessaires.

Il est même possible de traverser le réseau et de se connecter à une session REPL distante afin d'inspecter l'état de l'application sur un serveur de production.

ClojureScript prend également en charge les sessions REPL qui vous permettent de vous connecter au code exécuté dans le navigateur (ou Node), afin que vous puissiez tester et modifier votre programme. Un outil comme Figwheel vous permet également de visualiser immédiatement le résultat du ClojureScript modifié, sans même avoir à rafraîchir votre navigateur.

Tout cela signifie que Clojure est très adapté au codage en direct. Avec Clojure, vous pouvez même produire de la musique live. Pour cela, consultez la bibliothèque Overtone.

6. Clojure est pleine pile

D'une manière ou d'une autre, Java n'est pas une pile complète. C'est une langue back-end. Ce n'est pas que les gens n'essaient pas non plus de devenir pertinents au début. Mais sérieusement, qui développe encore des applications en utilisant AWT, Swing ou JavaFX? De même, Clojure a été initialement conçu comme une langue alternative alternative. Avec l'avènement de ClojureScript et la sortie ultérieure de nombreuses bibliothèques et outils frontaux, vous pouvez décidément considérer Clojure comme un langage de pile complet. Il a également introduit de nombreux éléments intéressants qui améliorent considérablement l'expérience des développeurs de pile complète.

Réagir

S'appuyant sur le succès de la bibliothèque JavaScript React, il est désormais incroyablement facile de créer un (n) SPA (application à page unique) en ClojureScript. Reagent est une bibliothèque ClojureScript qui simplifie l'écriture des composants React. La syntaxe Hiccup, pour décrire le balisage HTML à l'aide de structures de données Clojure, nécessite très peu de lignes de code.

Voici un exemple de composant qui compte le nombre de clics sur un bouton, le montrant ensuite dans un div:

clojure



(état de comptage def (atome 10))

(compteur defn []
  [:div
   @count-state
   [:button :on-click #(swap! count-state inc)
    "x"]])

(réactif / composant de rendu [counter]
                          (js / document.getElementById "app"))

Avec Om, un autre wrapper ClojureScript pour React, il était possible d'obtenir des performances encore meilleures que React lui-même. Cela a à voir avec une meilleure efficacité dans la comparaison de différents ensembles d'état dans ClojureScript (ainsi que dans Clojure lui-même).

7. Clojure empêche l'enfer de rappel

Un des problèmes auxquels les développeurs Javascript doivent faire face est un phénomène appelé «callback hell». Un programme de navigation n'a qu'un seul thread. C'est la raison pour laquelle nous devons travailler avec des rappels imbriqués. Comme nous appliquons des niveaux d'imbrication plus profonds, la lisibilité du code sera de plus en plus mauvaise.

La solution Clojure se compose d'une bibliothèque, core.async, qui – comme vous l'avez peut-être deviné maintenant – simplifie le travail avec du code asynchrone. Il va également de soi que cette bibliothèque fonctionne aussi bien sur le serveur que sur le client. Ses blocs de construction sont des canaux, des tampons et des blocs de départ. Les blocs Go sont des transformations sources qui offrent l'apparence d'un code synchrone, mais se traduisent finalement par des rappels imbriqués.

Un exemple. Le code suivant récupère d'abord l'adresse e-mail d'un utilisateur via un appel à une API REST. Pour cela, la bibliothèque http-cljs est utilisée, qui combine les appels Ajax avec core.async. En tant que valeur de retour, nous recevons un canal, à partir duquel nous pouvons lire le résultat en utilisant le <! (prendre) opération. Ensuite, nous utilisons l'adresse e-mail dans un deuxième appel, pour récupérer les commandes de cet utilisateur.



(allez (laissez [email (:body
                 (

8. Clojure a spec

Depuis Clojure 1.9, sorti fin 2017, Clojure est livré avec spec qui est une bibliothèque pour décrire la structure des données et des fonctions avec un support pour la validation, le rapport d'erreurs, l'instrumentation et la génération de données. Les développeurs venant de Java peuvent trouver le manque de typage statique de Clojure difficile à digérer. Bien que la spécification n'offre pas de garanties de temps de compilation au-delà de la vérification de la syntaxe des macros, elle offre quelque chose de beaucoup plus puissant à l'exécution.

Un petit exemple. Nous pouvons définir une spécification qui exprime un entier qui doit être pair et supérieur à 1000.



(s / def :: big-even (s / et int? even? # (>% 1000)))

Nous pouvons utiliser l'instrumentation pour vérifier si un argument d'une fonction est conforme à cette spécification:



(defn square-big-even
  "renvoie un carré de x, qui doit être un grand entier pair"
  [x]
  (* x x))

;; définir la spécification des arguments de fonction:
(s / fdef square-big-even: args (s / cat: x :: big-even))

;; instrument
(st / instrument `carré-grand-pair)

(carré-grand-pair 1)
;; => 1 - échoué: pair? à: [:x] spécification: utilisateur / grand-même

(carré-grand-pair 2000)
;; => 4000000

Les messages d'erreur de spec peuvent sembler un peu cryptiques. Des bibliothèques comme expound peuvent être utilisées pour transformer des données d'erreur de spécification en texte lisible par l'homme.

La même spécification utilisée pour la validation peut également être utilisée pour la génération de données et les tests génératifs:



(gen / sample (s / gen :: big-even))
;; => (150874 1402248 1562 165164 1146 2300 15226 1564 1686 3298)

9. Clojure est un bon langage de script

Dans presque tous les projets logiciels, il sera nécessaire d'écrire des scripts shell pour automatiser les tâches. Java n'est pas la solution la plus naturelle pour un langage de script car il est assez détaillé par rapport aux choix populaires comme Bash et Python. Cela nécessite une étape de compilation et le temps de démarrage de la JVM est un autre obstacle à franchir. Ces dernières années, plusieurs solutions sont apparues pour obtenir un environnement de script Clojure avec un bon temps de démarrage:

  • planck: basé sur ClojureScript et fonctionne sur JavaScriptCode
  • joker: un interprète Clojure implémenté dans Go
  • babashka: un interprète Clojure écrit en Clojure lui-même, compilé avec GraalVM native-image

Il s'agit d'un script Babashka qui affiche les chemins canoniques de tous les répertoires du répertoire actuel. Comme vous pouvez le remarquer, il existe une interopérabilité avec la classe java.io.File, l'une des nombreuses classes fournies avec babashka.



#! / usr / bin / env bb

(nécessite '[clojure.java.io :as io])

(defn canonical-dirs [path]
  (- >>
   (chemin io / fichier)
   (.listFiles)
   (filtre # (. isDirectory%))
   (carte # (. getCanonicalPath%)))))

(doseq [dir (canonical-dirs ".")]
  (dir. println))

Ce script prend environ 19 ms à exécuter sur mon Macbook Pro 2019 (de Michiel).

VOIR AUSSI: JavaScript en haut, les liens de Python avec Java dans les classements RedMonk

10. Clojure est stable

Comme Java, Clojure met fortement l'accent sur la stabilité des API. Les programmes écrits pour Clojure 1.0 en 2009 sont encore susceptibles de fonctionner avec Clojure 1.10.1 en 2020. La communauté Clojure dans son ensemble adopte la philosophie de Rich Hickey selon laquelle les API devraient croître au fil du temps par accrétion (ajout de fonctionnalités), relaxation (nécessitant moins), et correction de bugs au lieu de faire des changements de rupture. Regardez la keynote de spécification si vous voulez en savoir plus (transcription).

Épilogue

Java, le langage de programmation, a 25 ans. Pendant toutes ces années, de nombreuses alternatives à Java sont apparues et disparues. Mais de toutes celles qui sont apparues depuis «l'explosion cambrienne» des langues au cours de la décennie précédente, seules quelques-unes sont parvenues à maturité: Groovy, Scala, mais aussi Clojure. Dans cet article, nous avons répertorié un certain nombre de fonctionnalités qui séparent positivement Clojure des autres. Si vous êtes convaincu, la meilleure façon d'apprendre Clojure est de l'utiliser réellement.

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.