Uploaded avatar of xavdid

Un retour sur #12in23 par quelqu'un qui l'a terminé

@xavdid
il y a Plus de 2 ans

Ce billet est paru à l'origine sur le site de David et est republié ici avec son autorisation

En janvier dernier, Exercism a annoncé un nouveau programme appelé 12in23, qui invitait les participants à essayer 12 nouveaux langages de programmation en 2023. Chaque mois avait son thème (comme « Analytical April » ou « Object Oriented October ») et mettait en avant des langages précis à essayer. J'adore apprendre de nouvelles choses et je suis devenu un vrai mordu des langages (de programmation), alors j'ai décidé de me lancer. 12 langages, 12 mois !

Maintenant que l'année touche à sa fin, je suis vraiment ravi du déroulement du projet. J'ai réussi à essayer 12 nouveaux langages, rencontré des personnes formidables dans la communauté Exercism, et publié au passage quelques contributions open source sympas ! Dans ce billet, je les passe tous en revue et je raconte ce que chacun m'a apporté.

Choisis les langages

Je me suis fixé quelques règles pour l'année afin de tirer le meilleur parti de l'expérience :

  1. Les langages devaient soit m'être totalement nouveaux, soit suffisamment peu familiers pour que j'aie l'impression d'apprendre beaucoup.
  2. Les langages choisis devaient être (potentiellement) utiles à approfondir plus tard. Ce projet était juste pour le plaisir, mais je voulais passer du temps à apprendre des choses (au moins en partie) utiles.
  3. J'installais tout l'outillage local et l'extension VSCode pour chaque langage que j'utilisais. Je voulais comparer les langages sur un pied d'égalité, avec autant d'indications de type et d'IntelliSense que possible. À la fac, je faisais tous mes devoirs de programmation dans Sublime Text, sans aucun linter ni autocomplétion. Je craignais que, si j'apprenais à programmer avec tous ces outils, je dépende trop d'eux et ne devienne pas un bon programmeur. C'est finalement l'inverse qui s'est produit. Plus je délègue de réflexion à mes outils, plus je peux réfléchir au problème concret. Ne retiens pas les choses, retiens comment les retrouver.

C'est parti !

Janvier (sans thème)

Quand janvier a commencé, l'équipe Exercism était encore en train de choisir les thèmes mensuels, donc le choix du langage de ce mois-là était libre. Faute de direction, j'ai lancé l'année avec Go. J'avais suivi un cours accéléré sur ce langage courant 2022, mais je ne l'avais pas beaucoup utilisé depuis et je ne me sentais pas du tout à l'aise.

Go est un langage intéressant. Son compilateur strict garantit que Ton Programme Sera Correct, et tu ne peux pas avancer tant qu'il n'estime pas que c'est sans danger.1 Sa façon verbeuse de gérer les erreurs fait que tu n'as jamais de surprise (au prix d'écrire if err != nil { return err } un nombre incroyable de fois). Il rend bien service en simplifiant des choses difficiles (comme le parallélisme via les canaux), mais complique aussi des choses faciles (la manipulation de strings). Il dispose d'une bibliothèque standard solide, ce qui permet de mener à bien la plupart des tâches sans modules tiers. J'aime que la majeure partie de l'écosystème (formatage, installation, compilation, etc.) soit fournie par l'équipe et intégrée à la commande go. Ce langage a ses détracteurs, mais je trouve qu'il atteint largement ses objectifs de correction et de maintenabilité.

Je ne l'ai pas assez aimé pour en faire mon premier choix, mais c'est un excellent outil à avoir dans sa panoplie pour les programmes sensibles aux performances, comme l'affichage du chemin imbriqué dans l'invite de mon shell.

Functional February

Février a plongé directement dans le grand bain avec les langages fonctionnels, une branche mathématique des langages impératifs plus courants. Les langages fonctionnels sont connus pour leurs fonctions « pures » (sans effets de bord). J'ai choisi Elixir, surtout parce que mon ami Caleb l'a utilisé pour l'Advent of Code et en dit le plus grand bien.

J'ai beaucoup aimé travailler avec Elixir. Il s'inspire de Ruby (ce qui se vérifie : son créateur, José Valim, était un contributeur central de Rails). J'ai trouvé simple d'exprimer des concepts fonctionnels comme l'enchaînement de méthodes. J'ai adoré tout le sucre syntaxique qui rendait cela facile, comme l'opérateur pipe (|>) :

foo(bar(baz(new_function(other_function()))))
# becomes
other_function() |> new_function() |> baz() |> bar() |> foo()

C'était aussi la première fois que je travaillais avec des macros, c'est-à-dire du code qui écrit du code. Comme les programmes Elixir peuvent s'exprimer sous forme d'AST qui est elle-même du code Elixir valide, il est facile d'écrire du code pour en produire d'autre, valide. C'est un concept vraiment chouette qu'Elixir rend facile. J'ai aussi bien aimé la façon dont les fonctions peuvent faire du filtrage par motif sur la forme de leurs arguments, ce qui permet d'aiguiller les appels vers la bonne implémentation :

defmodule TuplePrinter do
  def print({a}) do
	IO.puts("single")
	IO.puts(a)
  end

  def print({a, b}) do
	IO.puts("double")
	IO.puts(a)
	IO.puts(b)
  end
end

TuplePrinter.print({1})
TuplePrinter.print({2, 2})

# single
# 1
# double
# 2
# 2

C'est le genre de fonctionnalité soit géniale, soit qui transforme ton code en véritable plat de spaghettis. Dans un cas comme dans l'autre, c'était un concept sympa !

Elixir tire aussi parti du fait de tourner dans la machine virtuelle BEAM d'Erlang, ce qui lui donne un vaste écosystème avec lequel communiquer. Il excelle en concurrence et constitue le cœur du très apprécié framework web Phoenix.

Même si je n'ai pas besoin d'Elixir tout de suite, j'ai trouvé très agréable de travailler avec lui, et c'est clairement un langage que je serais prêt à retrouver. En plus, c'était un défi intéressant d'aborder des problèmes familiers de façon inhabituelle (à savoir, de manière récursive).

Mechanical March

Mars était consacré aux langages « système », qui se compilent en code machine.

Parmi les options, Go était le seul langage qui m'intéressait.2 Les lecteurs attentifs remarqueront que j'avais déjà fait un mois de Go, donc le refaire ne compterait pas dans mes 12. Eh bien, quand je l'ai choisi pour janvier, ils n'avaient pas encore annoncé qu'il y aurait des thèmes, donc je ne voyais pas que je me peignais dans un coin.

Si j'avais su que Bun allait arriver, j'aurais probablement essayé Zig, mais malheureusement je ne pouvais pas (encore) voir l'avenir. Donc, faute de choix plus convaincant, j'ai repris un mois de Go supplémentaire, en sachant qu'il me faudrait doubler à un moment plus tard dans l'année.

Analytical April

Avril portait entièrement sur les langages populaires en science des données. Je connaissais trop bien Python et j'avais fait du R dans un cours de stats à la fac (sans aimer ça), donc ce sera Julia !

J'ai apprécié travailler avec, mais surtout parce qu'il ressemblait tellement à Python. C'était un peu déstabilisant, comme être américain au Canada. Tout semble très familier, mais avec un petit quelque chose de décalé, difficile à mettre le doigt dessus. Soudain, quelqu'un te tend une pièce de 2 dollars (ou une fonction vraiment taillée pour le calcul matriciel) et tu réalises que tu n'es plus au Kansas.

Ce qui m'a le plus marqué, c'est le système de types de Julia. Il s'annotait (facultativement) comme le système de types de Python, mais comportait des vérifications à l'exécution pour s'assurer que les arguments correspondaient à leurs types déclarés. Je pense que le système de Python trouve le bon équilibre entre l'intégration aux outils et le fait de ne pas te gêner, mais j'avoue que les erreurs à l'exécution de Julia pour les fonctions mal typées étaient aussi utiles.

Au final, Julia était sympathique, mais je ne m'attends pas à en avoir besoin à l'avenir.

Mindshifting May

Mai a mis les bouchées doubles sur le « essaie quelque chose de nouveau » en mettant en avant des langages qui font des choses très inhabituelles. J'en ai profité pour essayer le toujours très populaire Rust. Je dois dire que je comprends l'engouement.

Même si le tristement célèbre vérificateur d'emprunts demande un temps d'adaptation, j'ai aimé la façon dont il m'a poussé à réfléchir plus attentivement à mes programmes. Le compilateur était certes strict, mais ses messages d'erreur m'aidaient bien au-delà du nécessaire à corriger les problèmes. Je ne dirais pas que j'étais particulièrement productif la première semaine, mais j'ai au moins l'impression de voir le sommet de la courbe d'apprentissage.

cargo, le gestionnaire de paquets de Rust, mérite aussi une mention spéciale. Je n'ai installé aucun paquet tiers, mais ses fonctions de compilation, de test et de formatage étaient excellentes. Idem pour son extension VSCode, qui offrait tous les agréments que j'attends d'un langage à typage statique comme Rust. Une bonne expérience de développement fait vraiment toute la différence.

Même s'ils sont très différents au niveau de l'implémentation, Rust m'a paru proche de Go dans l'usage que j'en ferais : faire tourner des programmes très rapidement. Beaucoup d'outils dans des langages que j'utilise régulièrement se tournent vers Rust pour ses performances, donc je m'attends à le voir de plus en plus à l'avenir (même si je n'écris pas de Rust moi-même).

Summer of Sexps (June)

Juin était le mois des S-expressions, une forme syntaxique courante dans les lisps. J'ai choisi Clojure, un langage fonctionnel qui tourne sur la JVM.

J'avais écrit un peu de Clojure il y a longtemps. Je venais de finir mes études et je suis devenu le seul responsable d'un script quotidien critique pour l'entreprise à mon premier job. Inutile de dire que c'était une période délicate. J'étais curieux de voir si, maintenant que j'étais plus vieux et plus sage, ce serait plus abordable.

Je suis heureux de pouvoir dire que oui ! L'expérience fonctionnelle de février m'a aidé à penser récursivement, et la syntaxe n'était pas si terrible quand on s'y plongeait. Son interopérabilité avec la JVM serait aussi utile si je l'utilisais dans un projet plus important.

Je ne me vois pas utiliser Clojure tant qu'il existe des alternatives, mais ce n'était pas une expérience désagréable pour autant.

Quête annexe : l'Universal Test Runner !

Pendant des années, j'ai utilisé une petite fonction bash pour lancer les tests unitaires dans le répertoire courant. Comme je travaillais dans tous ces nouveaux langages, je me suis retrouvé à y ajouter des lignes par commodité ; me souvenir de lancer t était bien plus facile que de réapprendre sans cesse la commande de test propre à chaque langage.

À mesure que la logique nécessaire dépassait mon niveau de confort avec bash, j'ai pris un peu de temps en juin pour en faire un projet à part entière : l'Universal Test Runner.

Je l'ai partagé sur le forum Exercism et j'ai été bien accueilli. Ça leur a tellement plu que nous avons décidé d'intégrer une fonctionnalité similaire directement dans l'Exercism CLI (écrite en Go, un sujet que je venais fortuitement de réviser). Donc pour la seconde moitié de l'année, je pouvais lancer exercism test pour exécuter la suite de tests du langage de ce mois-là (une commande nativement prise en charge dans l'Universal Test Runner).

Si tu veux en savoir plus sur le processus, j'en ai parlé bien plus en détail au moment de son lancement.

Bref, passons à la suite !

Jurassic July

Juillet mettait à l'honneur les vieux langages. Le choix était assez maigre ce mois-là côté praticité. J'ai commencé par le vénérable COBOL, ayant entendu qu'il faisait encore tourner beaucoup d'infrastructures critiques. Mais comme un mariage début août approchait à grands pas, je n'avais pas la bande passante pour m'asseoir et apprendre un langage aussi éloigné de ce que je connaissais. Je me suis donc rabattu sur Visual Basic comme option la moins mauvaise.

Il n'y a pas grand-chose à dire ici. Le langage semblait un peu verbeux, mais assez facile à utiliser. D'après ce que j'ai compris, il était surtout conçu pour le développement d'interfaces sous Windows, donc avec de petits exercices, il est difficile de bien se faire une idée de l'ensemble.

Appy August

Août regorgeait de langages pour créer des applis. Sans surprise, il y avait beaucoup de choix ce mois-là. Je me suis tourné vers Swift. En tant qu'utilisateur de nombreux produits Apple, leur langage maison m'est assez pertinent. Je n'y étais pas totalement nouveau : j'ai publié une seule appli iOS en 2016, écrite entièrement en Swift. Mais je n'y avais plus touché depuis, et le langage a beaucoup évolué, donc j'ai estimé qu'il comptait quand même.

J'ai été agréablement surpris par sa facilité d'utilisation. Contrairement à beaucoup d'autres langages ici, Swift est assez récent. Il est sorti pour la première fois en 2014 et a clairement profité des leçons de la conception moderne des langages. Il a un gestionnaire de paquets fourni par l'éditeur, le chaînage optionnel, des fonctions de première classe et une interpolation de string sensée. Il était ergonomique à lire et à écrire, même sans utiliser Xcode.

Cela dit, Swift est surtout utile pour les applis des plateformes Apple, que je ne développe pas actuellement. Même s'il a bien fonctionné pour les exercices, je ne compte pas y revenir de sitôt. J'adore pouvoir l'écrire sur mon iPad, par contre !

Slimline September

Septembre explorait des langages très concis ou minimalistes. Je me suis tourné vers jq, un outil que j'utilise et adore depuis des années.

Je l'ai toujours considéré comme un simple outil pour manipuler du JSON, pas comme un langage de programmation généraliste. J'ai été agréablement surpris de constater qu'il a tout l'attirail habituel (fonctions, variables, boucles, etc.), ce qui m'a permis d'écrire des programmes assez complexes :

# input: { "series": "1", "sliceLength": 1 }
. as {series: $series, sliceLength: $sliceLength} |
if
  $series == "" then
	"series cannot be empty" | halt_error
  elif $sliceLength > ($series | length) then
	"slice length cannot be greater than series length" | halt_error
  elif $sliceLength == 0 then
	"slice length cannot be zero" | halt_error
  elif $sliceLength < 0 then
	"slice length cannot be negative" | halt_error
  else
	.
end
| [range(0; $series | length)]
| map($series[. : . + $sliceLength])
| map(select(. | length == $sliceLength))

C'était amusant d'essayer toutes les fonctionnalités de jq dont je n'ai jamais eu besoin pour de simples transformations de données. Même si l'outillage était un peu limité (pas d'intégration à l'éditeur, etc.), une meilleure compréhension de l'étendue des fonctionnalités de jq était précieuse.

édit : DJ Adams sur Mastodon m'a fait découvrir le projet jq-lsp et son extension VSCode associée. Je suis passé à côté cette fois, mais j'y jetterai un œil à l'avenir.

Object Oriented October

Octobre s'est penché sur les langages orientés objet. J'ai un faible pour les conceptions orientées objet, qui reflètent de près la façon dont je me représente les programmes dans ma tête. Je me suis tourné vers Ruby, ce qui peut sembler un choix étrange.

Je travaille chez Stripe, où se trouve la plus grande base de code Ruby au monde. Ça ne compterait sûrement pas comme un langage « peu familier », si ? Même si tout cela est vrai, notre monolithe Ruby est très éloigné du Ruby « standard » : tout est vérifié statiquement avec Sorbet, il y a beaucoup de génération de code, et on fait pas mal de magie pour que tout fonctionne ensemble et passe à l'échelle. Même si le Ruby chez Stripe et ailleurs reste au fond le même langage, travailler à des échelles si différentes donne des expériences très différentes ; je voulais savoir à quoi ressemblait la vie à l'extérieur (depuis les années où j'utilisais Ruby intensivement).

Globalement, c'était bien ! Ruby lui-même est excellent et cite le « bonheur du programmeur » parmi ses objectifs principaux, ce qui m'a parlé. J'aime pouvoir souvent deviner le nom de fonctions de la bibliothèque standard que je n'ai jamais utilisées. J'aime la facilité avec laquelle on construit du code fonctionnel, et le côté ergonomique et expressif de la syntaxe.

Cela dit, j'ai été surpris de voir à quel point l'outillage de développement était en retard sur Python. J'ai peut-être été gâté, mais avoir des indications de type dans l'éditeur et une analyse statique et un formatage extrêmement rapides compte pour moi plus que je ne le pensais. Pour un langage aussi populaire que Ruby à son apogée, j'ai été surpris de le sentir si en retard à cet égard.3 Je ne me suis jamais vraiment habitué non plus aux parenthèses facultatives dans les appels de fonction, ce qui rendait le passage de fonctions en arguments moins direct.

Ruby reste un excellent langage et je continuerai à l'utiliser au travail, mais il ne m'apporte rien que Python ne fasse déjà, du moins pour l'instant.

Nibbly November

Novembre a été le mois le plus difficile : les langages d'assemblage. Même s'il n'est plus courant de les écrire à la main, c'est un sujet utile et intéressant à connaître. J'ai choisi WebAssembly pour son importance pour le web moderne et futur. Même s'il sert généralement de cible de compilation (et non de langage qu'on écrit à la main), il existe de l'outillage pour les acharnés dans le coin.

Je me suis senti étonnamment bien préparé pour ce mois. La syntaxe rappelait Clojure et la structure du langage rappelait TIS-100 de Zachtronics. Bizarrement, j'ai aimé devoir repartir de zéro pour chaque opération : ça avait un charme désuet. Je détesterais devoir vraiment produire quoi que ce soit de cette façon, mais c'était une curiosité amusante sur le moment. Avec des commentaires généreux, j'ai pu écrire quelque chose de presque lisible :

(module
  (func (export "eggCount") (param $number i32) (result i32)
	(local $res i32) ;; result
	(local $remainder i32) ;; loop counter

	(loop $loop

  	;; $res =
  	(local.set $res
    	;; $res +
    	(i32.add
      	(local.get $res)
      	;; $number % 2
      	(i32.rem_u
        	(local.get $number)
        	(i32.const 2)
      	)
    	)
  	)

  	;; $number //= 2
  	;; (keep on stack)
  	(local.tee $number
    	(i32.div_u
      	(local.get $number)
      	(i32.const 2)
    	)
  	)

  	;; this will keep looping until remainder is 0
  	br_if $loop
	)

	local.get $res
  )
)

Le plus gros obstacle était le manque de documentation et de ressources. C'était difficile même de savoir quelles fonctions globales étaient disponibles. Mais comme je ne vais pas vraiment utiliser ça, une fois lancé, ça ne m'a pas trop dérangé.

Décembre a conclu l'année avec des langages qui n'entraient dans aucune autre catégorie. À cause du doublon de mars, je devais compléter deux langages ce mois-ci.

J'ai commencé par Wren. Créé par Bob Nystrom, célèbre entre autres pour Crafting Interpreters. J'ai été séduit par son souci du détail, sa petite empreinte et sa conception descendante ; tout semble très bien pensé. Ce niveau de soin se voit dans les détails des règles de portée des variables et de confidentialité. Son compilateur est petit et abondamment commenté, ce qui en fait une excellente ressource d'apprentissage si les implémentations de langages t'intéressent.

Wren est un peu brut et semble largement abandonné, mais je trouve que c'est acceptable pour un langage jouet. Personne ne s'attend à ce qu'il soit prêt pour la production. Il y a certainement de la place dans le monde pour des langages non destinés à la production.

Aussi : Lua

Mon second choix ce mois-ci était Lua. Contrairement à Wren, il est incroyablement pratique. Sa facilité d'intégration fait qu'on le retrouve partout, comme dans le scripting Redis et les mods Factorio. Le modèle objet demandait un peu d'adaptation, mais je vois comment je pourrais être productif rapidement. Je me suis vite attaché aux tables comme structure à tout faire. L'outillage était bon : le gestionnaire de paquets fonctionnait sans configuration, et l'extension VSCode prenait en charge les annotations de type en commentaire sans histoire.

Même si je n'ai rien pour quoi j'ai besoin de Lua tout de suite, c'est un autre excellent outil à avoir sous la main vu son utilisation répandue.

On conclut

J'ai plus aimé ce tour d'horizon des langages que je ne l'aurais cru. Non seulement j'ai acquis de nouvelles compétences pratiques, mais mes horizons se sont vraiment élargis.

Quant à la suite, je pense apprendre beaucoup plus de Rust. Son importance dans le paysage de l'outillage de développement est désormais évidente, et je veux être capable de lire et de contribuer aux outils sur lesquels je m'appuie.

Je n'ai pas de livrable précis en tête, mais j'ai tout le livre sur Rust à lire, un cours Rust pour devs JS que j'ai fait passer en note de frais une année, et tout un parcours Exercism à terminer. J'aimerais contribuer à au moins un projet open source (probablement Just, un de mes nouveaux programmes préférés), mais on verra où l'année me mène.

D'ici là, joyeuses fêtes et bonne fin d'année 2023 !

  1. Les variables inutilisées provoquent une erreur de compilation ?? Franchement, quoi ↩

  2. En fait, j'ai d'abord essayé le C++ (que je n'avais pas écrit depuis la fac). Ce n'était juste pas amusant, donc j'ai abandonné ↩

  3. C'est une autre façon dont le Ruby « réel » diffère de mon expérience chez Stripe, donc je suis content d'avoir pu essayer les deux ↩

5e Jan 2024 · Tu l'as trouvé utile ?