- La programmation de gauche à droite permet au programme de rester valide dès la saisie du code, ce qui maximise la prise en charge par les outils comme l’autocomplétion de l’éditeur
- Les list comprehensions de Python nuisent à l’autocomplétion à cause de variables non déclarées et de l’absence d’inférence de types
- Rust et JavaScript permettent de construire naturellement le programme de gauche à droite, ce qui rend l’usage des variables et l’exploration des méthodes plus intuitifs
- Le style fonctionnel en C et en Python nuit à une expérience de codage efficace à cause de la faible découvrabilité des noms de fonctions ou des structures
- Pour les logiques complexes, un code qui se déploie de gauche à droite est plus facile à lire, avec une meilleure maintenabilité et extensibilité
Programmer de gauche à droite
Le code doit être valide dès le moment où on le saisit
Les limites des list comprehensions en Python
- La syntaxe de list comprehension en Python
words_on_lines = [line.split() for line in text.splitlines()]pose problème, car elle oblige à accéder à une variable non déclarée (line), ce qui empêche l’éditeur de fournir correctement l’autocomplétion ou l’inférence de types - Pendant la saisie partielle du code
- si l’on tape
words_on_lines = [line.sp, l’éditeur ne connaît pas le type delineet ne peut donc pas suggérer de méthodes - il devient aussi difficile de détecter des erreurs potentielles comme une faute de frappe dans le nom de variable (
lime, par exemple)
- si l’on tape
- Pour obtenir des suggestions correctes, il faut écrire du code inachevé, ce qui rend le processus peu intuitif et inconfortable
Construction de gauche à droite en Rust
- Dans l’exemple Rust (
let words_on_lines = text.lines().map(|line| line.split_whitespace());)- la variable (
line) est considérée comme déclarée au moment même où elle apparaît pour la première fois avec la fonction anonyme, ce qui permet immédiatement l’autocomplétion et les suggestions de méthodes - en pratique, la méthode
split_whitespacea elle aussi été facile à trouver grâce aux suggestions automatiques
- la variable (
- Cette approche permet au programme de rester toujours partiellement valide, ce qui permet à l’IDE ou à l’éditeur d’aider à coder en temps réel
Progressive Disclosure et utilisabilité des API
- La progressive disclosure est un principe de conception selon lequel l’utilisateur n’est exposé qu’au niveau de complexité dont il a besoin, et il peut aussi s’appliquer à la programmation
- Exemple : une UX de traitement de texte où les options liées à une image n’apparaissent qu’au moment où l’on ajoute une image
- Le langage C offre peu de support dans ce domaine
- comme toutes les fonctions liées à
FILE *filene se découvrent pas viafile., il faut mémoriser les conventions de nommage (fread,fclose, etc.), ce qui rend les fonctionnalités difficiles à découvrir - à l’inverse, dans un langage idéal, les suggestions de méthodes via
file.permettraient de découvrir progressivement les fonctions associées
- comme toutes les fonctions liées à
Différence de découvrabilité entre fonctions et méthodes
- Comparaison entre les exemples Python
map(len, text.split())et JavaScripttext.split(" ").map(word => word.length)- en Python, comme les noms de fonctions tels que
len,lengthousizene sont pas prévisibles, il faut souvent essayer plusieurs variantes avant de trouver ce qui fonctionne réellement - en JavaScript, il suffit de taper
.laprèsword.pour que l’éditeur proposelengthet d’autres méthodes, ce qui améliore fortement la découvrabilité - même des fonctions d’ordre supérieur comme
maprendent immédiatement plus clairs la valeur de retour réelle et le type de données
- en Python, comme les noms de fonctions tels que
Les avantages d’une écriture structurée quand la logique se complexifie
- Dans les logiques complexes (long code Python avec
filteretlambdaimbriqués)- il faut vérifier à répétition le début et la fin du code, et les conditions ou parenthèses appariées entraînent une baisse de lisibilité et une compréhension plus difficile
- Dans la version JavaScript de la même logique, on peut lire et comprendre le code de façon séquentielle, de haut en bas et de gauche à droite
Principe fondamental
Le code doit être valide à chaque instant de la saisie
- Le programme reste valide même avec la seule saisie de
text - Même après avoir écrit
text.split(" "), puis en poursuivant avec.map(word => word.length), l’état intermédiaire reste toujours valide dans son ensemble - Ce type de codage augmente la possibilité d’un support en temps réel par l’éditeur et, dans un environnement REPL, permet aussi de voir immédiatement le résultat
Conclusion
- Les API et le design des langages doivent permettre de saisir naturellement le code de gauche à droite, tout en créant à chaque étape intermédiaire un programme valide
- Une bonne conception d’API est la clé de cette amélioration de l’expérience de programmation
1 commentaires
Commentaires Hacker News
SELECTet non parFROM, ce qui rend moins évident de voir immédiatement quelle entité (table) est concernée, et cela gêne aussi les éditeurs intelligents pour aider plus efficacement à écrire les requêtes ; aller dans l’ordre FROM -> SELECT -> WHERE paraît plus naturel, d’autant plus qu’on nomme les colonnes dans la clause SELECT puis qu’on les référence dans WHERE ; en fait, on pourrait même imaginer que la clause SELECT soit implicite et n’écrire queFROM tableau lieu deSELECT * FROM table; je sais que ce genre de plainte me fait passer pour un vieux grincheux, mais c’est juste une lubie personnelleTABLE <table>qui peut être intéressanteforqui fixe le type ; tout le monde s’inquiète de la souplesse du typage Python, mais il est étrange que la seule syntaxe qui fixe clairement le type de retour — la compréhension de liste — soit prise pour cible ; dans la plupart des autres langages, Rust compris, l’ordre n’est pas non plus « from iter as var » ; et il est aussi amusant de comparer la syntaxe des appels de fonctions selon les langages (Python a aussifunctools.map)returnmanquant, etc.){3} for {2} in {1}; ce genre d’outils offre un compromis entre une « syntaxe facile à lire » et une « syntaxe facile à taper » ; de mon côté, même si cela passe par de l’outillage, je préfère privilégier une syntaxe plus lisible ; il n’y a pas de raison de s’accrocher absolument à la structure « for-in »bake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12), utiliser un pipe commemix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12)est bien plus simple et lisiblepipede pandas en Python, on obtient ceci En R, cela donne Les deux sont assez lisibles, mais l’avantage de R est que le pipe fonctionne mieux aussi en dehors des dataframesI drink water→Ich trinke Wasser), pas systématiquement tout à la finfrom some_library import child_moduleest très intuitive ; en JS, une structure commeimport { asYetUnknownModule } from SomeLibraryme paraît nettement moins intuitivefrom; on pourrait simplement écrire et ce serait très bienlen(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))), il vaut mieux utiliser un array NumPy, ce qui évite de créer inutilement de nouvelles listes en mémoire et permet de traiter toute la ligne d’un coup ; par exemple reflète beaucoup mieux cette logique « de gauche à droite »line > 0est acceptable, les règles de broadcasting peuvent devenir complexes), mais les API de collections dans des langages au typage plus strict, comme dans l’exemple JavaScript de l’auteur ou en C#, Java et Scala, sont plus propres ; personnellement, je préfère Kotlin, parce qu’on peut écrire ceci