Blog des Gens Compliqués

Le tiercé de langages qui fait tout

06/03/2020 13:25:29+01:00|Par DkVZ
Informatique & Web
15 minutes de lecture (facile)

Table des matières

Introduction

Cet article était censé être une brève puis ça s'est avéré dépasser le seuil de brévité pourtant extrêmement flottant que je m'autorise.

Je vous rassure on va quand même essayer de faire moins long que l'article qui précède celui-ci (qui a écrasé tous les records).

Voilà le topo: je suis sorti de l'école il y a 1000 ans avec des connaissances correctes en Java et PHP. Avec PHP appris en extra-scolaire (quelqu'un se souvient de EasyPHP :D?).

On avait aussi chipoté Perl dans son cas d'utilisation "tripoter des fichiers texte" du genre "sors moi une liste des adresses IP uniques présentes dans ce fichier log". Ce qui est un peu surprenant de la part de ce professeur parce qu'il est plutôt du genre à demander de créer des implémentation de linked lists et autres transformées de Fourier de machins bidules (c'était un mathématicien je pense) — mais pas pour le cours de Perl.

Ce cours était génial, parce que non seulement il m'a forcé à apprendre les regex à leur source, mais en plus j'allais continuer à m'en servir pour mon quotidien de sysadmin, puisque Perl s'applique aussi bien à investiguer/transformer des fichiers logs qu'à rédiger des petits scripts d'automation — l'interprêteur Perl étant installé par défaut sur Mac et Linux (peut-être plus sur Mac aujourd'hui? Sais pas).

Perl fonctionnait sous Sierra en tous cas:

Capture d'écran montrant l'interprêteur Perl sous OSX
C'est pas en JS avec tous les Prettier et "format on save" qu'on verrait du code en forme de chameau

Pour terminer, on a eu du C/C++. Avec examen sur papier. La vraie expérience rustique quoi.

Ma pratique du C++ consistait plus ou moins à faire l'équivalent local d'un parcours exercism.

Créer un buffer circulaire ça sert à rien du tout pour vous préparer au code de production et à réellement travailler sur des projets en C/C++ qui sont générallement des programmes desktop classiques avec interface graphique (comme Gimp ou LibreOffice), des serveurs à haute perf (Nginx, Apache) voire des noyaux de système d'exploitation ou des moteurs de jeux.

De plus, C/C++ avaient déjà 10000 ans à l'époque avec plusieurs environnements de compilation, il fallait essayer de capter comment ce truc make fonctionne et "linker" avec des librairies locales et euh... Regarder un programme C/C++ se compiler après avoir tapé "make" reste encore à ce jour une expérience mystique à part.

J'ai compilé un PHP 7.2 il y a quelques semaines et après avoir exécuté 1460 tests il m'a sorti qu'il y "avait eu une segmentation fault mais c'est ptet normal lol". Classe.

Animation montrant un terminal pendant une compilation
Regarder la compilation de gros projets en C++ c'est un des grands miracles de la vie

Etant donné qu'on peut faire des applis desktop en Java et que l'écosystème était beaucoup plus clair, je n'ai jamais considéré C/C++ comme un de mes outils.

Résumé de mes cas d'utilisation

J'arrivais plus ou moins à tout couvrir avec ces outils:

Langage Applications
Java
  • Web app avec moulte processing ou avec destination gros serveur
  • Desktop app avec Swing ou JavaFX
PHP
  • Scripts shell occasionnels
  • Web app modeste ou avec base de code PHP existante
Perl
  • Scripts shell trop compliqués pour bash (le code bash est horriblement indigeste à lire, je suis désolé (oui, Perl aussi c'est indigeste à lire, c'est tout dire à quel point je trouve que bash c'est l'horreur))
  • Processing logs, fichiers texte, etc.

Je n'utilise aucun langage système (type C++). Il y a une lacune à combler de ce côté-là.

Le remplaçant de Perl

Je dois beaucoup à Perl mais il est vraiment temps qu'il prenne sa retraite. Vous croyez que les gens nés après 2000 en ont déjà entendu parler? C'est l'équivalent d'une cassette VHS. Ou d'un DVD je suppose. Mince, je suis si vieux que ça?

Quand je pense que Perl était aussi utilisé pour créer des applications web... Je trip toujours sur les bons vieux look non-responsive avec une vieille nuance de bleu en arrière plan et du Time-New-Roman partout.

Regardez-moi ce site officiel gourvernemental qui porte fièrement "cgi/welcome.pl" dans son URL, ce qui est à peu près équivalent à porter un t-shirt "Je change de slip une fois par mois" en public, bien entendu sans support HTTPS parce que ce serait un peu déconné quoi:

Capture d'écran du site Perl du moniteur Belge
Oui, cette disposition est faite de tout un tas de tags <table>

Le langage de script le plus usité aujourd'hui est probablement Python. Je suis pas fan de Python. Trop alien, mes yeux ont vraiment du mal à parcourir cette syntaxe à base d'indentation.

Je m'en sers occasionnellement, par ex. l'autre jour j'avais besoin de générer une liste d'adresses email aléatoires (pas de questions SVP) et le premier résultat de recherche c'est un script Python que je peux adapter.

Mon véritable remplaçant de Perl pour mes cas d'utilisation script et transformation/analyse de fichiers (quand c'est pas PHP — C'est parfois PHP) c'est JavaScript. On s'en doutait pas, hein?

S'il s'agit d'écrire un petit script rapide, je suis extrêmement efficace en JS. Il a tout le support regex que l'on puisse espérer et un puissant système de flux si on doit analyser des fichiers logs de 14 GB (ce qui m'arrive, oui), le langage est extrêmement flexible avec son modèle d'objets et les listes ont une puissante API fonctionnelle qui permet de faire tout un tas de trucs classe et frimer en utilisant reduce partout.

Bon et alors j'ai mes réserves avec npm et installer 100 MB de dépendances (dont LODASH lel) pour faire un petit machin mais il faut bien avouer que TOUT est sur npm y compris des projets extrêmement utiles pour le scripting comme Inquirer.

Node.JS n'est installé par défaut sur aucune plateforme mais son installation est devenue extrement simple qu'il s'agisse de Linux, Mac ou Windows.

Pour d'autres hack rapides (besoin d'un serveur machin vite fait, petit reverse proxy, ...) JS est excellent puisqu'il est naturellement cloisonné à 1 (+ workers) thread et est extrêmement efficace (dans le sens ne crame pas toutes vos ressources pour en plus être lent) pour tout ce qui est I/O avec relativement peu de traitement.

Pour finir, c'est pas comme si je venais de publier un article immense sur JS. J'ai passé beaucoup trop de temps avec ce langage, autant le rentabiliser.

Le remplaçant de Java

Je vous aurais pas cru si je m'étais lu 5 ou 6 ans plus tot, mais mon remplaçant de Java est sans hésitations C# et .NET Core 2.1+.

En terme de langage seulement j'ai toujours préféré C#. Moins verbeux, plus flexible, plus moderne pour des tas de raisons.

Le problème c'est que c'était pour Windows uniquement et que Microsoft avait une drôle de réputation à l'époque dont ils trainent encore des séquelles aujourd'hui mais ils ont la chance d'être en comparaison avec Google et Facebook qui sont difficiles à battre en terme de drôle de réputation.

Mieux, depuis .NET Core, le runtime officiel n'est plus cloisonné à Windows uniquement. Enfin dans 90% des cas (il y a même SQL SERVER POUR LINUX LOL).

Mais Dick, pourquoi t'utilises pas JavaScript pour faire des applications web backend?

C'est vrai, Express et ses copains c'est facile, efficace, robuste. Mais... On a quand même une grosse impression de programmer en mode Fischer Price. Rangez vos fourches, vous savez que j'ai raison.

Vous pouvez ajouter autant de couches de Typescript et de DTO et autres trucs bidules, ça reste un langage interprêté largement considéré comme mono-thread, où tous les nombres sont à virgule flottante. Entre autres choses.

Héberger un mini-backend de blog sur Node c'est très bien, mais pour quelque chose de plus consistent... Il va falloir soit déployer dans une infrastructure de type Kubernetes qui est considérablement plus complexe qu'un ou deux gros serveurs, ou bien il va falloir chipoter avec des process managers du genre de PM2 et ça complique pas mal l'aventure là où une application monolithique Java ou C# peut gérer une charge énorme sans nécessairement devoir passer 5 heures à tout optimiser ou compter sur un plan "LE TURFU" comme les AWS Lambda.

Un de mes nombreux tweets qui parlent de mes problèmes de Kubernetes
Pour ceux qui ne sont pas au courant, je suis un grand fan de Kubernetes

Je vous juge pas si vous êtes en full JAVASCRIPT SERVERLESS mais en fait si je vous juge très fort.

Revenons à Java: sa grande force selon moi est son parallélisme. Avec son threading userspace bien rodé ce n'est pas rare de voir des processus Java qui entretiennent des miliers de threads avec une étonnante relative tranquilité.

C'était le candidat idéal pour lancer une web app sur un gros serveur plein de ressources. Contrairement à PHP, une web app Java peut facilement gérer un pool de connexions DB et traiter énormément de requêtes en parallèle sans devoir chaque fois tout reconstruire, ce qui permet également de conserver tout un tas de trucs dans le gigantesque heap (qui est une zone de la mémoire, j'explique au cas où) utilisé par les apps Java en général.

Depuis Java 6 on a eu le rachat par Oracle, il a fallu attendre perpettt pour que les fonctions lambda soient supportées et puis les langages basés sur des classes et uniquement ça ont perdu pas mal de vitesse, le fonctionnel étant bien plus à la mode (et d'ailleurs à l'origine de l'obsession pour les lambda).

Le langage reste très utilisé en production mais il a fini par se diviser en plusieurs nouveaux langages plus modernes, moins verbeux (genre mieux quoi) qui compilent également en bytecode Java et sont dès lors compatibles avec tout l'écosystème existant de Java (SKDJFHdksjfh CE BON VIEUX MAVEN).

J'ai voulu apprendre Scala à l'époque, qui est un de ces langages. Il est plein de chouettes idées et beaucoup, beaucoup moins verbeux avec une très forte influence fonctionnelle, le support des traits entre autres choses.

Le problème c'est que personne n'utilise ce langage. Il y a bien le Play Framework mais... Ouais. Chercher une solution sur Stackoverflow ça peut rapidement sentir la tristesse là où un bon copier-coller immédiat se serait montré pour un autre langage plus populaire.

Suite de tweet racontant l'histoire d'un bug qui a été copié collé dans plusieurs applications depuis Stackoverflow
Longue histoire à base d'avoir un peu trop copié-collé Stackoverflow

Je vous épargne Groovy et Kotlin qui sont un peu dans la même situation bien que Kotlin ait eu une certaine heure de gloire quand c'était le langage "officiel" pour Android (ça l'est peut-être toujours mais y a Flutter maintenant (et React native LOL)).

Alors pourquoi C# et .NET Core? Même si les langages basés sur des classes ça pue le vieux, C# est bourré de touches modernes en tout genre. Je pourrais vous en parler pendant des heures.

Si vous venez de Java, le simple fait que var existe est sans doute responsable d'une baisse de plusieurs MB de texte dans des projets conséquents.

Exemple de code Java typique:

ComplexBusinessObjectWithAShittyLongName cbo = new ComplexBusinessObjectWithAShittyLongName();
cbo.setName("COUCOU");
cbo.setStupid(true);
cbo.doStuff();

En C#:

var cbo = new ComplexBusinessObjectWithAShittyLongName() {
  Name = "COUCOU",
  Stupid = true
};
cbo.doStuff();

Alors déjà, j'avais oublié mais C# a le concept de propriétés qui incluent leurs getters et setters, alors qu'avec Java il faut absolument extraire des méthodes get set toutes verbeuses qui sont toujours les mêmes et qui puent.

Ensuite, C# permet d'initialiser les propriétés juste après avoir appelé le constructeur, et je ne dois pas réécrire l'horriblement long type de cette variable. Ca n'a peut-être l'air de rien mais juste var crée une énorme différence de verbosité.

Je me répète mais je pourrais parler de détails de productivité augmentée en C# par rapport à Java pendant tout un article. Certains de ces détails ont été proposés pour intégration à Java mais ça prend 1000 ans.

Bon allez, comme je suis lancé, vous avez entendu parler de null coalescing?

C'est le principe d'avoir un raccourci pour ne pas devoir chaque fois vérifier si une variable est null avant d'appeler une méthode dessus (ce qui pourrait potentiellement sortir de bonnes vieilles NullPointerException qui font pas sérieux).

Illustration, Java d'abord:

if (machin != null && machin.truc != null) {
  machin.truc.doSomething();
}
En C# on peut écrire la même chose ou bien utiliser ceci:
machin?.truc?.DoSomething();

C'est tout. Et ce ne sont pas les seules constructions du genre qui soient disponibles pour écrire du code plus concis.

A côté de ça, C# dispose d'événements, de méthodes d'extensions, d'une puissante API de réflexion, d'enum, d'une type de données Struct qui est sur le stack, Linq qui ajoute tout un tas d'opérations fonctionnelles chaînables sur les collections, qsldfjqmsdlfkjqmsdlkfj CET ARTICLE EST DEJA TROP LONG.

A vrai dire et pour donner un peu de pain à Java, j'ignore si le threading .NET est aussi balaise que celui de Java, on dirait qu'ils utilisent un hybride intelligent threads systèmes et threads userspace. Ils ont également une implémentation d'async/await qui va bien et me fait penser que ça doit forcément être plus efficace que Java (qui a tendance à utiliser énormément de CPU pour lancer ses 10000 threads par seconde).

En gros, il me faut une web app consistante et robuste qui va tourner sur un gros serveur (ou un moyen serveur, ça le fait aussi)? .NET Core sous Linux! Yeah.

Le petit nouveau

Comme expliqué dans l'intro, le C/C++ c'est pas trop mon truc, je perds beaucoup trop de temps sur la chaîne de compilation et utiliser des librairies et comprendre les 15-20 ans d'évolution du langage et de ses outils c'est trop chaud.

Je n'avais donc rien pour remplir le cas d'utilisation "il me faut un truc extrêmement performant qu'on peut installer directement sans interprêteur".

D'autant plus qu'utiliser C/C++ n'est pas sans danger. C'est très simple de compiler un truc qui plante avec une erreur de segmentation, il suffit d'accéder à un élément au delà de la taille d'un tableau. Le compilateur ne va pas vous en empêcher.

Je suis sur que Linus dirait "BAH OUAIS TU VEUX QU'ON TE TIENNE LA MAIN AUSSI?" — Ben... Oui, j'aime l'amour et les papillons.

La gestion de la mémoire est aussi réputée pour être une horreur puisqu'elle doit être gérée manuellement.

Personnellement je ne pense pas qu'il s'agisse d'une "horreur", j'aime bien ce genre d'artisanat manuel; celà dit je suis bien obligé d'avouer que ça augmente les chances de faire des bêtises, et ce d'autant plus que le projet est volumineux et que plusieurs personnes travaillent dessus.

Bon et alors les langages qui font un usage important de classes ça reste plus trop la mode. Tout comme utiliser des "goto" en C qui est un peu l'autre opposé polaire.

J'aurais pu me mettre à Go, langage système compilé qui inclus un garbage collector pour gérer les soucis de gestion de mémoire. Mais du coup c'est quoi ce truc, un genre d'hybride qui ressemble à utiliser pyinstaller pour faire un genre d'exécutable Python?

J'imagine que c'est plus performant mais la réalité c'est qu'on se traine un HELLO WORLD qui fait 2 MB, et même si c'est acceptable de nos jours moi ça me choque dans mon âme.

Entre en scène Rust qui porte malheureusement le même nom qu'un jeu vidéo.

Ce langage:

  • Est sans rapport avec Google ni Microsoft (enfin pour l'instant);
  • Assure une sécurité de mémoire sans devoir tout faire manuellement mais sans devoir inclure de garbage collector qui augmente de 1 MB la taille de l'éxécutable non plus;
  • Dispose de toutes les primitives pour facilement programmer du multithreading avec des mécanismes intégrés de protection contre les race conditons;
  • On est libres de s'amuser comme on veut avec des références par-ci par-là (histoire d'économiser la mémoire au max - impossible dans d'autres langages) sans avoir l'impression de perdre le contrôle puisque le compilateur détecte (pratiquement) toutes vos bêtises;
  • Pas de valeur null, c'est bête mais c'est du génie. OK en fait c'est juste du génie. On utilise un type particulier pour représenter le fait qu'une valeur puisse être absente, la gestion d'erreur est également propre et nette, dans le même genre (pas de 12 km de try-catch de la mort comme en C# et JavaScript);
  • Contient toutes les constructions de langage modernes, du fonctionnel (fonctions dans des variables, fonctions passées en paramètres, ...), des traits qui permettent un genre de polymorphisme et des comportements homogènes, de la surcharge d'opérateurs, des closures, PLEIN DE TRUCS;
  • Et ça continue d'évoluer.

Bon, évidemment, pour accomplir ce niveau de magie, le compilateur vous crie beaucoup dessus et les règles à suivre sont strictes, parfois un peu étranges, et il faut pas être allergique au message d'erreur.

Ce qui a plusieurs effets secondaires intéressants:

  • C'est super satisfaisant d'avoir le truc qui compile après avoir résolu 14 erreurs une par une;
  • On a l'impression de coder quelque chose d'extrêmement solide et paré à toutes les éventualités - Particulièrement en multithreading où je dois vous avouer que j'ai fait pas mal de trucs sauvages;
  • Le côté solide et sérieux est tellement proéminent que j'ai CARREMENT ECRIT DES TESTS (je vous jure).

Vous vous rappelez de quand je parlais de null coalescing qui existe en C# et JS (mais pas Java) et comment c'est bien pratique? En Rust ce n'est même pas nécessaire puisqu'il n'y a pas de valeur null!

Et la gestion d'erreur sans try catch... Je ne peux pas exprimer à quel point c'est trop cool. Il y a un opérateur ("?" - évidemment) qui propage tout seul les erreurs dans une méthode qui retourne un Result (qui est le type de données utilisé pour la gestion d'erreur).

Reste plus qu'à créer votre type d'erreur personnalisé (si vous voulez) et implémenter des traits de conversion depuis les erreurs qui vous voulez intercepter, puis tout se fait tout seul. OHMAGAD.

Petit point négatif: Go est toujours le plus populaire de la classe et jouit d'un plus vaste écosystème mais j'ai bon espoir que ça s'égalise avec le temps.

Ceci dit, beaucoup de choses sont directement disponibles à la création d'un nouveau projet. Par ex. il ne faut pas de "framework" de tests, tout a été pensé pour écrire des tests directement dans les fichiers de code et/ou dans un répertoire "tests" séparé — pas de test runner à installer et marier, ça roule tout seul.

Rust, c'est bien.

Commentaires

Il faut JavaScript activé pour écrire des commentaires ici

Ajouter un commentaire

Votre commentaire a été ajouté
(enfin, je pense)