GoSuda

Comparaison de la conception linguistique dans les environnements de génération de code par LLM : coût d'abstraction et explicitation en Go

By Rabbit Princess views ...

Langages modernes

Les langages modernes désignent les langages qui offrent, par rapport aux langages antérieurs, une productivité, une stabilité, une sécurité et une maintenabilité supérieures. Ces langages intègrent activement les techniques et concepts récents et fournissent aux développeurs des outils de développement plus efficaces et plus sûrs. Parmi les langages modernes représentatifs figurent Java, Rust, Python, TypeScript, entre autres, et ceux-ci présentent les caractéristiques suivantes.

1. Conception orientée objet ( OOP )

Les langages modernes sont pour la plupart conçus sur la base des concepts de la programmation orientée objet. La programmation orientée objet prend en charge des principes tels que l'encapsulation, l'héritage et le polymorphisme, et elle rend la structure des programmes claire et souple en décomposant les systèmes complexes en objets de petite taille gérés séparément. Cette approche est particulièrement efficace pour maîtriser les coûts et l'effort de développement lors de la conception de logiciels de grande envergure, et pour réduire les problèmes susceptibles de survenir lors de la maintenance.

2. Sucre syntaxique et expressions

Les langages modernes proposent divers sucres syntaxiques et une syntaxe fondée sur les expressions, afin d'améliorer la lisibilité du code et la productivité des développeurs. Des fonctionnalités telles que l'opérateur ternaire, les expressions lambda et le filtrage de motifs (pattern matching) réduisent le code répétitif (boilerplate), rendent le code prévisible et améliorent la productivité des développeurs.

3. Système de modules

Les langages modernes permettent de séparer un programme en plusieurs unités et de les gérer de manière distincte grâce à un système de modules. Cela facilite la réutilisation du code et la gestion des dépendances, et rend la maintenance aisée même lorsque la taille du projet augmente. Parmi les exemples représentatifs figurent Maven/Gradle pour Java, Cargo pour Rust et npm/yarn pour TypeScript.

4. Langages intermédiaires et machines virtuelles

Les langages modernes adoptent des langages intermédiaires et des machines virtuelles afin de garantir l'indépendance vis-à-vis des plateformes, l'optimisation des performances et la sécurité. Parmi les exemples représentatifs figurent JVM, LLVM, WASM et GraalVM.

Le développement à l'ère de l'IA et les limites des langages modernes

Les langages modernes sont nés de la prémisse selon laquelle les humains rédigent la majeure partie du code. Naturellement, l'objectif des langages modernes est de réduire les tâches répétitives, d'accroître la productivité et, grâce à des structures favorisant la collaboration entre développeurs, de réaliser efficacement de grands logiciels. Toutefois, à l'ère de l'IA, cette prémisse s'effondre progressivement. Des outils fondés sur l'IA, tels que Copilot et Cody, automatisent une grande partie de la rédaction du code, et la quantité de code qu'un seul développeur peut produire augmente de manière spectaculaire. Par conséquent, des caractéristiques des langages modernes, perçues autrefois comme des avantages, se transforment peu à peu en inconvénients dépassés.

Conception orientée objet

L'IA peut analyser rapidement et avec précision les structures dans lesquelles toute l'information est explicitée au sein d'une seule fonction ou d'un seul module. En revanche, à mesure que le contexte s'accroît, le nombre de segments à inférer augmente, de sorte que la productivité et la précision de l'IA diminuent. Le code écrit en OOP répartit la logique entre de nombreux objets au lieu de la regrouper en un seul endroit, ce qui exige en conséquence davantage de contexte de la part de l'IA. Examinons le code ci-dessous.

 1public class AnimalHandler {
 2    public void handle(Animal animal) {
 3        animal.speak();
 4    }
 5}
 6
 7public class Main {
 8    public static void main(String[] args) {
 9        AnimalHandler handler = new AnimalHandler();
10
11        Animal a1 = new Dog();
12        Animal a2 = new Cat();
13        Animal a3 = new Horse();
14
15        handler.handle(a1);
16        handler.handle(a2);
17        handler.handle(a3);
18    }
19}

Dans le code ci-dessus, pour déterminer le comportement réel de la méthode speak(), l'IA doit répéter le raisonnement suivant

  1. De quelle classe concrète animal est-il l'instance ?
  2. Où est définie la méthode speak() redéfinie dans cette classe ?
  3. Où se trouvent les définitions des classes Dog et Cat, et quel est leur comportement interne ?
  4. Existe-t-il une possibilité que les classes Dog et Cat soient redéfinies dans d'autres classes ?

Ces informations ne sont pas regroupées dans un seul fichier ou une seule fonction ; elles ne peuvent être comprises qu'en suivant les relations entre les fichiers de classes et la structure d'héritage. De plus, les éléments déterminés à l'exécution, comme la réflexion ou le chargement dynamique, constituent pour l'IA une véritable boîte noire, rendant l'analyse pratiquement impossible.

Sucre syntaxique et expressions

L'IA privilégie les comportements explicites aux comportements implicites, et préfère composer des structures simples pour aboutir à une solution unique plutôt que d'utiliser des structures complexes. En revanche, le sucre syntaxique admet diverses formulations tout en ayant souvent un comportement interne identique ou similaire. L'IA doit apprendre individuellement la signification de ces formulations variées, et il peut devenir difficile de déterminer quelle syntaxe recommander et selon quelle priorité dans une situation donnée.

Langages intermédiaires et machines virtuelles

L'IA apprend principalement à partir du code source. En revanche, le langage intermédiaire (bytecode) supprime, lors de la compilation, la signification des noms de variables et autres éléments, et doit être réinterprété par la machine virtuelle au moment de l'exécution. Il s'agit d'une tâche très difficile à comprendre ou à transformer pour l'IA. Par exemple, pour l'IA, la conversion de TypeScript vers Go est possible, mais convertir le bytecode du moteur V8 en bytecode JVM ou en code machine relève pratiquement de l'impossible.

Le véritable langage moderne de l'ère de l'IA

Ce qui précède nous permet de constater que les caractéristiques des langages modernes sont en réalité des éléments dépassés, peu adaptés à l'ère de l'IA. Quel langage pourrait alors devenir le langage moderne le plus adapté à l'ère de l'IA ? Une réponse peut être trouvée dans les éléments que les langages modernes passés ont rejetés. Le langage moderne de l'ère de l'IA doit

  1. Disposer d'une syntaxe explicite et d'un enchaînement naturel.
  2. S'affranchir de la structure OOP excessive et adopter une structure plate et simple.
  3. Privilégier un ensemble restreint de mots-clés de base pour réaliser diverses fonctionnalités, plutôt que des sucres syntaxiques superflus ou des schémas implicites.
  4. Disposer d'un système de build simple et prévisible, et non d'un système de build complexe.

Le langage qui répond le mieux à ces critères est Golang. Le langage Go a été conçu pour permettre de réaliser la plupart des fonctionnalités pratiques avec un ensemble minimal de mots-clés essentiels et une combinaison simple de syntaxes, et il exclut rigoureusement les abstractions superflues ainsi que les comportements implicites. Cela constitue une structure favorable tant pour les développeurs que pour l'IA lorsqu'elle interprète et génère du code.

Ainsi, les caractéristiques de Go, qui privilégient une structure prévisible et plate, un flux explicite et une syntaxe cohérente, s'accordent mieux à un environnement de développement où l'IA participe activement, contrairement aux langages modernes existants conçus autour des développeurs humains. La philosophie de conception conservatrice de Go, jugée autrefois dépassée, devient ainsi, à l'ère de l'IA, le choix le plus avancé.