BT

Le projet Lambda vu de l'intérieur. Une interview avec Brian Goetz

Écrit par Vikram Gupta , traduit par Nouhoum Traore le 30 sept. 2013 |

Au mois d’avril dernier, Oracle a annoncé le retardement de la très attendue sortie de Java 8 jusqu’au premier trimestre de 2014 comme rapporté sur InfoQ.

Mark Reinhold, l’architecte en chef du groupe s’occupant de la plate-forme Java chez Oracle, a dit sur son blog :

Le travail le plus important ayant été enlevé du milestone 6 est lié au Projet Lamda, la fonctionnalité la plus importante de cette “release”. Nous avons intégré les changements apportés au langage et à la VM pour le Lambda, à la fin de l’année dernière ; mais entre toutes les parties qui n’arrêtent pas de changer et le travail sur la sécurité, cela a pris plus de temps que prévu d’apporter la touche finale à l’API des streams et aux améliorations des librairies associées.

InfoQ a discuté avec Brian Goetz, responsable de la JSR-335, au sujet de ses observations depuis l’intérieur sur le Projet Lambda.

InfoQ : Il y a beaucoup de coordination sur ce projet, allant des collections parallèles à la nouvelle API des Streams. Avec votre regard depuis l’intérieur du projet et sans divulguer de secrets, pouvez-vous nous donner un aperçu sur la JSR 335 ? Comment toute cette coordination est menée ?

Brian : En effet, le nombre d’interactions entre les différentes pièces mobiles est intimidant et il y avait un nombre important d’acteurs distincts : les listes de deux groupes d’experts très étroitement liés (l’un pour les fonctionnalités purement liées au langage et l’autre, plus général, incluant les membres de la JSR-166, pour les fonctionnalités de la librairie), la communauté de l’OpenJDK, et plusieurs équipes de composants au sein du groupe s’occupant de la plate-forme Java chez Oracle. Mais cela valait le coup, contrairement à certains efforts d’évolution antérieurs de la plate-forme où nous avons tout implémenté à travers du sucre syntaxique dans le compilateur, la JSR-335 a été en mesure de procéder à une évolution conjointe du langage, des librairies et de la VM, ce qui débouche sur un résultat globalement bien meilleur.

A mon avis, la clé du succès ici est de maintenir une orientation claire pour les objectifs. Les fonctionnalités du langage ne sont pas, en elles mêmes, un objectif mais elles offrent des possibilités et encouragent ou dissuadent certains styles ou idiomes. Même avec la catégorie “ajouter les expressions lambda”, il y a souvent des objectifs cachés qui conditionnent l’approche. La proposition BGGA avait un objectif sous-jacent qui était de supporter l’abstraction des contrôles à travers des librairies ; CICE est axé sur l’objectif plus modeste de soulager le désagrément syntaxique des classes internes ; quand les lambda ont été ajoutés à C# de nombreux cas d’utilisation ont été dictés par les besoins de LinQ.

Pour la JSR-335, nous avons maintenu l’objectif clair selon lequel les fonctionnalités du langage sont avant tout un moyen d’avoir de meilleures librairies telles qu’avoir la possibilité de faire des opérations parallèles en masse sur les collections existantes. Les fonctionnalités du langage permettent de meilleures librairies ; ce qui permet d’écrire du code utilisateur plus simple, plus clair et moins sujet aux erreurs. Donc, nous avons recensé un catalogue d’idiomes utilisateurs que nous souhaitons rendre possibles tels que :

   int sumOfWeights =
       anArrayList.parallelStream()
              .filter(b -> b.getColor() == BLUE)
              .mapToInt(b -> b.getWeight())
              .sum();

Nous avons utilisé des exemples comme pierres de touche afin de nous assurer que nous avançons vers nos objectifs au lieu de bouger seulement. Les points suivants sont implicitement dans cet exemple :

  • la nécessité d’encoder de manière compacte des comportements dans des expressions (traiter le code comme de la donnée)
  • la nécessité de déplacer le contrôle des itérations (en utilisant par exemple des constructions comme la boucle for) du client aux librairies (itération interne)
  • la nécessité d’ajouter de nouvelles méthodes à des types existants comme List.parallelStream (évolution de l’interface)
  • la nécessité d’avoir une meilleure inférence de type (de sorte que le compilateur puisse inférer les types des lambda réguliers.
  • la nécessité de pouvoir faire des opérations parallèles sur des structures de données non “thread-safe”

L’un des plus grands défis quand on ajoute des fonctionnalités importantes au langage est le risque de régression. Quand on est déjà en train d’effectuer un changement important il y a toujours la tentation d’en ajouter quelques autres pendant qu’on y est. Nous avons des centaines de suggestions de la communauté allant dans ce sens et devions dire non à la quasi-totalité. Avoir un ensemble d’objectifs précis par rapport au périmètre de ce travail a été clé pour dire “cela pourrait être cool mais il est hors du périmètre”.

Un autre outil de coordination clé était l’utilisation de modèles mathématiques formels dans la conception des fonctionnalités du langage comme les règles d’héritage des méthodes par défaut ou le comportement de l’inférence de type. Le langage naturel est particulièrement mal adapté pour discuter de problèmes complexes comme ceux-là ; les malentendus s’y glisseraient à coup sûr. L’utilisation de modèles formels nous a permis de discuter et de comprendre les propositions de sémantiques pour une fonctionnalité donnée d’une manière précise et non ambiguë, et nous a servi de modèle pour la spécification, la réalisation et les tests.

InfoQ : Il semble que les activités du Projet Lambda se déroulent au sein d’OpenJDK. Comment cela a-t-il amélioré la participation de la communauté ?

Brian : Nous avons, sur la liste de diffusion du développement des lambdas, une équipe de personnes ayant très tôt adopté les lambdas, qui téléchargent régulièrement les dernières versions (ou compilent depuis le code source) et essaient les nouvelles fonctionnalités. Ce retour d’expérience est absolument capital pour le processus. Ces personnes trouvent des bogues et des problèmes de facilité d’utilisation pendant que le coût des corrections est faible. La chose la plus précieuse que la communauté peut faire pour aider est : essayez vous-même le code et rapportez votre expérience (bonne ou mauvaise).

InfoQ : La quasi-totalité des JSR depuis la création de Java ont strictement porté sur des changements affectant la phase de compilation, Invoke-Dyanamic étant l’exception notable. Y-a-t-il des changements au niveau du bytecode dans la JSR-335 ?

Brian : L’implémentation des expressions lambda s’appuie fortement sur invokedynamic, introduit dans Java SE 7. Avoir invokedynamic dans la boîte à outils du développeur du compilateur nous a permis d’éviter de faire beaucoup de travail sur la VM que nous aurions été tentés de faire autrement. Cependant nous avons ajouté deux fonctionnalités ayant un impact sur la VM ; les méthodes par défaut (la VM doit être impliquée dans l’héritage qui au final affecte les bytecodes invokevirtual, invokeinterface et invokespecial) et les méthodes statiques dans les interfaces (principalement un assouplissement des restrictions existantes sur le classfile).

InfoQ : Il semble que IntelliJ Idea supporte partiellement la JS-335. Je suppose que les vendeurs d’IDE ont eu, il y a quelque temps, des versions en avant-première pour travailler. Savez-vous quand nous pourrions avoir le support de la JSR par NetBeans et Eclipse ?

Brian : Nous nous sommes assurés que tous les vendeurs sont représentés dans le groupe des experts afin que les fonctionnalités soient supportées par les outils et que les vendeurs d’IDE puissent avoir un accès rapide aux spécifications. Depuis longtemps NetBeans a des builds qui ont les lambda activés ; ils ont une longueur d’avance puisqu’ils utilisent javac comme compilateur. IntelliJ supporte la JSR-335 de manière significative à la fois pour les versions en cours de développement et les produits finis ayant pour version 12.x. Bien sûr, tout cela attend la version finale de la spécification ; jusque-là rien n’est définitif.

InfoQ : L’une des caractéristiques intéressantes du projet Lambda est l’aspect fonctionnel avec l’API des Streams et les collections concurrentes. Pourriez-vous nous en parler ? Comment ont-ils été conçus, implémentés et coordonnés ?

Brian : Une grande partie du travail est partie des cas d’utilisation que nous souhaitions rendre faciles pour les utilisateurs. Nous avons regardé des librairies d’autres langages ainsi que des librairies Java comme LambdaJ en accordant une attention particulière aux exemples “mis en avant” et avons utilisé ces exemples comme un catalogue des “besoins requis” pendant l’exploration des modèles possibles pour les supporter. Nous avons également estimé qu’il était important que les opérations sur les Streams fonctionnent à la fois de manière séquentielle et en parallèle et que les collections existantes, même celles qui ne sont pas “thread-safe” , puissent être utilisées comme sources des Streams. Nous avons prévu trois itérations pour la conception de l’API s’appuyant sur ce que nous avons appris de l’itération précédente.

InfoQ : Lambdas ou Closures, il y a un débat sur la différence. Quel est votre point de vue sur le sujet ?

Brian : Je pense que s’énerver sur le fait que les lambdas tels qu'exprimés dans Java SE 8 sont de “vraies” closures ou non est une activité très peu constructive. Pour moi, l’utilisation du terme “vrai” devrait être une erreur de syntaxe ; il existe beaucoup de langages différents avec des constructions qui s’apparentent aux closures avec des degrés de similitude et de différence variables. Déclarer que le langage X a défini ce qu’est une vraie closure et que tout autre langage qui ne fait pas exactement de cette façon n’est pas réel n’est pas utile.

Cela dit, il y a des décisions difficiles à prendre quant au support des fonctionnalités, comme les captures mutables locales, les flux de contrôle non locaux, la transparence des exceptions ainsi que d’autres fonctionnalités ; et en abandonner certaines signifie que certains cas d’utilisation seront plus difficiles à exprimer naturellement. Ce sont des compromis entre complexité et expressivité, et nous avons essayé de dépenser notre budget de complexité là où il a le plus d’impact pour les développeurs. Il est normal de parler des avantages et inconvénients de chacune de ces décisions mais le faire en terme de “vraies closures contre fausses closures” n’est pas un moyen constructif d’avoir ce dialogue.

InfoQ : Les closures semblent introduire un paradoxe en ce qui concerne les interfaces. Nous pensons aux interfaces comme étant des structures qui sont dépourvues d’implémentation mais pouvant contenir des données. D’un autre côté les closures traitent réellement l’implémentation comme de la donnée. Alors le Projet Lambda nous autoriserait-il à définir et à implémenter des fonctionnalités dans une interface à travers la définition de champs contenant des closures constantes ?

Brian : Le code est de la donnée (Gödel nous a enseigné cela il y a près de 100 ans). Simplement, les expressions lambda rendent syntaxiquement plus facile l’expression d’un traitement pouvant être traitée comme de la donnée.

Cela dit, l’idée que les interfaces sont dépourvues d’implémentation est en train de changer avec Java SE 8. Pour supporter l’évolution des interfaces nous permettons qu’une interface puisse fournir des méthodes avec des implémentations par défaut qui sont héritées par les classes (une interface avec des méthodes par défaut peut être considérée comme un trait sans état). Les interfaces peuvent également avoir des méthodes statiques.

InfoQ : Le projet Lambda introduit un type “Optional”. Comment cela aide à éliminer les références nulles ?

Brian : Notre utilisation du type Optional est très limitée. Nous nous en servons essentiellement comme type de retour des méthodes qui pourraient ne rien retourner. Prenez une méthode comme Map.get(key) qui retourne null si la clé spécifiée n’est pas dans la Map. Cela souffre de deux défauts graves. Le premier est que null pourrait être une valeur valide pour les éléments d'une Map (certaines Maps permettent cela). Et là, vous ne pouvez plus faire la différence entre les cas “non présent” et “mappé sur null” et si vous devez faire un second appel à containsKey() pour déterminer le bon cas vous risquez une “race condition”. Le second défaut est qu’il est très facile d’écrire du code qui oublie de vérifier les nuls, ce qui rend votre code moins fiable (les vérifications des nuls rendent votre code plus laid). Bien sûr, il est trop tard pour sauver Map.get() mais nous n’allons pas continuer à faire la même erreur.

Un “Optional” peut soit décrire une valeur non nulle, soit être explicitement vide ; vous ne pouvez pas de manière aveugle déréférencer la valeur de retour d’une méthode optionnelle (dont le type de retour est un objet de type Optional) car le système de type vous en empêche. Vous devez donc explicitement décider ce qu’il faut si l’Optional est vide. Optional a différentes méthodes pour obtenir son contenu : get() (qui lance une exception si l’Optional est vide), getOrElse(defaultValue), getOrThrow(Supplier), etc. Ainsi vous pouvez explicitement mais discrètement dire ce que vous comptez faire lorsque des valeurs sont absentes - lancer une exception, substituer une valeur par défaut, etc.

Cela dit, ceux qui sont familiers avec le type Option de Scala sont susceptibles d’être déçus. Ce n’est pas un changement profond du système de type c’est simplement une classe utilitaire dans la librairie pour refléter explicitement “cette méthode pourrait ne rien renvoyer” au lieu d’utiliser nul pour cela.

Pour suivre l'avancement du projet Lambda, vous pouvez visiter la page du projet Lambda sur le site de l'OpenJDK.

A propos de l’interviewé

Brian Goetz est l’architecte du langage Java à Oracle et le responsable de spécification pour la JS-335 (Expressions lambda pour le langage Java). Il est l’auteur du best-seller “Programmation concurrente en Java” et présente régulièrement dans les grandes conférences du domaine.

Bonjour étranger!

Vous devez créer un compte InfoQ ou cliquez sur pour déposer des commentaires. Mais il y a bien d'autres avantages à s'enregistrer.

Tirez le meilleur d'InfoQ

Donnez-nous votre avis

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

M'envoyer un email pour toute réponse à l'un de mes messages dans ce sujet
Commentaires de la Communauté

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

M'envoyer un email pour toute réponse à l'un de mes messages dans ce sujet

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

M'envoyer un email pour toute réponse à l'un de mes messages dans ce sujet

Discuter

Contenu Éducatif

Rien ne serait possible sans le soutien et la confiance de nos Sponsors Fondateurs:

AppDynamics   CloudBees   Microsoft   Zenika
Feedback Général
Bugs
Publicité
Éditorial
InfoQ.com et tous les contenus sont copyright © 2006-2014 C4Media Inc. InfoQ.com est hébergé chez Contegix, le meilleur ISP avec lequel nous ayons travaillé.
Politique de confidentialité
BT