Un chti peu de lambdades lambdas à lombre du beffroiRémi Forax Décembre 2012
MoiMCF à luniversité Paris Est Marne-La-ValléeJoue avec Java depuis trop longtemp pourlavouerCréateur de langage dynamique...
Les lambdaaaaahsPourquoi  par ce que Java cétait trop simple ??Comment  on va tout casser Java ??Sous le capot  surtout ne...
Partie IIntroduction aux lambdas
Dit papa, cest quoi des lambdas ?private static ArrayList<File> subDirectories(File directory) {  ArrayList<File> director...
Parties codantesprivate static ArrayList<File> subDirectories(File directory) {  ArrayList<File> directories = new ArrayLi...
Seconde versionprivate static File[] subDirectories(File directory) {  return file.listFiles(new FileFilter() {      @Over...
Parties codantesprivate static File[] subDirectories(File directory) {  return file.listFiles(new FileFilter() {      @Ove...
Problème des classes anonymesUne classe anonyme est pas super adaptée  visuellement    Verbeux, rapport signal/bruit pas s...
Comment font les autres langages ? Lisp/Clojure closures Ruby block Groovy Closure Scala/C# lambda Javascript/Python anony...
Lexcuse multicoreOn a plein de coeurs et on sait pas quoi en faire !Si une partie du code est transformable en objetOn peu...
Sans lambda ...private static File[] subDirectories(File directory) {  return file.listFiles(new FileFilter() {      @Over...
Avec une lambda ...private static File[] subDirectories(File directory) {  FileFilter filter = (File path) -> path.isDirec...
Syntaxe pour les expressionssans paramètre  () -> System.out.println("welcome to the land of shtis")avec un paramètre (inf...
Syntaxe pour les instructionssans paramètre  () -> {    System.out.println("welcome to the land of shtis");  }avec un para...
SémantiqueTypé par une functional interface (ex SAM)  Runnable, Callable, Filter, Function, ...Une lambda nest pas un obje...
En utilisant linférenceprivate static File[] subDirectories(File directory) {  return file.listFiles(     path -> path.isD...
Comment linférence marche ?le compilo regarde le(s) type(s) target(s)  FileFilter filter = path -> path.isDirectory();lint...
Method Referenceprivate static File[] subDirectories(File directory) {  return file.listFiles(     File::isDirectory  );}p...
Method ReferenceRaccourçi si la lambda délègue juste à uneméthodeOn utilise :: rien que pour embéter les C++euxSi la métho...
Et les collectionsAvoir des lambdas cest bien, mais sanssupport au niveau des APIs ...Nouvelle interface java.util.Stream ...
Full Streamprivate static ArrayList<File> subDirectories(                                   File directory) {  return Arra...
Avec tout dans le main()La méthode subdirectories() sert pas vraiment !public static void main(String[] args) {  File dire...
Capturer la valeur de variables locales  Et si je veux tous les sous-répertoires dont le nom  commence par args[0]  public...
Pipeline délementsArrays.asStream(array)collection.stream()         filter     filter      blockiterator.stream()         ...
Method reference sur une instanceIl est possible de créer une méthode réference sur uneinstancepublic static void main(Str...
En résuméJava permet de définir des lambdas et desréférences sur des méthodesUne lambda est une expression ou une fonction...
Partie IIChangements pour Java
Changements pour JavaGros problèmes  Inférence déjà existante pas assez puissante  Interface pas extensibleEt des petits +...
Améliorer linférenceJava 5/7 infére  les variables de type des méthodes    List<String> list =Array.asList("foo", "bar")  ...
Inference avec Java 8Inference pour les types des lambdas  donc de gauche à droite comme les <>Inference pour appel de mét...
Les demi-dieux de linférenceDan Smith    Maurizio Cimadamore
Extensibilité des interfacesOn veux obtenir un Stream à partir duneCollection  collection.stream()On ne peut pas ajouter u...
TraitUn type contenant des méthodes abstraites etdes méthodes concrètes (mais pas de champs)Ajouter une méthode si lon fou...
but, you broke Java !
Default methodPermet de fournir un code par défaut qui est utiliser si il nennexiste pasinterface Iterator<T> {   public b...
SémantiqueLa méthode par défaut nest utilisée que“par défaut”interface A {  default void m() { ... }}class B implements A{...
Héritage mutiple ??interface A {  default void m() { ... }}interface B {  default void m() { ... }}class C implements A, B...
Héritage mutipleinterface A {  default void m() { ... }}interface B {  default void m() { ... }}class C implements A, B { ...
Partie IIIdans les entrailles de la VM
Au menu ...Comment les méthodes par défautfonctionnent ?Comment les lambdas sont compilés ?Comment les lambdas sont optimi...
Méthode par défautLe compilo ne peut rien faire !  Sinon on doit recompiler toutes les librariesDoit être fait par la VM  ...
Méthode par défaut et erasureinterface Foo<T> {  default T getFoo() { return ...; }}interface Bar {  String getFoo();}clas...
Méthode par défaut et erasureinterface Foo<T> {  default ObjectT getFoo() { return ...; }}interface Bar {  String getFoo()...
Méthode par défaut et erasureinterface Foo<T> {  default ObjectT getFoo() { return ...; }}interface Bar {  String getFoo()...
Compiler une lambda naïvementOn créé une méthode synthetic pour le corps de la lambdaOn crée une classe anonyme lors de la...
Lambda objet constant ?A lexécution, il y a deux sortes de lambdas  Les lambdas qui ne captures pas de variable ou les  mé...
Compiler vers une classe anonyme ? Et on se récupère tous les problèmes de perf des classes anonymes De plus, si une lambd...
Invokedynamic to rule them allOn veut un mécanisme qui délai linitialisation aupremier accès  invokedynamicOn veut un méca...
Compiler une lambdaiterator.forEach(dir -> System.out.println(dir));devientiterator.forEach(invokedynamic bootstrap [lambd...
Lambda ProxyInstance dune classe qui contient le pointeur defonction (MethodHandle) vers la lambda àexécuterIl ny a besoin...
Lambda Proxy en pseudo codeLe code Java correspondant est à peu près :public class Proxy$Block implements Block { private ...
Optimizations lors de lappelAvoir 1 seul classe pour 1 interface permet à la VM deremplacer lappel à linterface par le cod...
Optimizations lors de lappelSi un objet constant dans une boucle, il est sortie de la boucleinterface Iterator<T> {   ... ...
dans le monde merveilleux              des licornesOn guarde la boucle avec un test sur le method handle(comme pour lOn St...
Le léger hicLe code de la VM qui crée le lambda proxy et qui loptimiseest pas prêt  Le plan est prêt depuis longtemps, ces...
En résuméLa première version des lambdas sera pas laplus optimiséeLAPI dans java.util doit être fini pour finjanvierLa spe...
Si vous avez du temps libreet même si vous nen avez pas !Downloader la dernière version du jdk8 avec leslambdas  http://jd...
Java 8 : Un ch'ti peu de lambda
Prochain SlideShare
Chargement dans…5
×

Java 8 : Un ch'ti peu de lambda

5 482 vues

Publié le

Slides de la session du Ch'ti JUG animée par Remi Foxax sur Java 8

0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
5 482
Sur SlideShare
0
Issues des intégrations
0
Intégrations
3 662
Actions
Partages
0
Téléchargements
67
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Java 8 : Un ch'ti peu de lambda

  1. 1. Un chti peu de lambdades lambdas à lombre du beffroiRémi Forax Décembre 2012
  2. 2. MoiMCF à luniversité Paris Est Marne-La-ValléeJoue avec Java depuis trop longtemp pourlavouerCréateur de langage dynamique ou pasExpert pour les JSR 292 (invokedynamic) etJSR 335 (lambda)Contributeur OpenJDK, ASM, Tatoo, PHP.reboot, JDart,etc...
  3. 3. Les lambdaaaaahsPourquoi par ce que Java cétait trop simple ??Comment on va tout casser Java ??Sous le capot surtout ne pas toucher à la VM ??
  4. 4. Partie IIntroduction aux lambdas
  5. 5. Dit papa, cest quoi des lambdas ?private static ArrayList<File> subDirectories(File directory) { ArrayList<File> directories = new ArrayList<>(); File[] files = directory.listFiles(); for(File file: files) { if (file.isDirectory()) { directories.add(file); } } return directories;}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  6. 6. Parties codantesprivate static ArrayList<File> subDirectories(File directory) { ArrayList<File> directories = new ArrayList<>(); File[] files = directory.listFiles(); for(File file: files) { if (file.isDirectory()) { directories.add(file); } } return directories;}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  7. 7. Seconde versionprivate static File[] subDirectories(File directory) { return file.listFiles(new FileFilter() { @Override public boolean accept(File path) { return path.isDirectory(); } });}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  8. 8. Parties codantesprivate static File[] subDirectories(File directory) { return file.listFiles(new FileFilter() { @Override public boolean accept(File path) { return path.isDirectory(); } });}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  9. 9. Problème des classes anonymesUne classe anonyme est pas super adaptée visuellement Verbeux, rapport signal/bruit pas satisfaisant sémantiquement On veut envoyer une expression, créont une classe ... performance création dun objet à chaque appel +1 classe sur le disque +1 instance de java.lang.Class + métadata en mémoire
  10. 10. Comment font les autres langages ? Lisp/Clojure closures Ruby block Groovy Closure Scala/C# lambda Javascript/Python anonymous function Python comprehension/C# Linq Pourquoi introduire les lambdas en Java ? les classes anonymes couvrent déjà le besoin ??
  11. 11. Lexcuse multicoreOn a plein de coeurs et on sait pas quoi en faire !Si une partie du code est transformable en objetOn peut distribuer / paralleliser lexécutionPresque magique Enfin, si on a un effet de bord, on est mort !
  12. 12. Sans lambda ...private static File[] subDirectories(File directory) { return file.listFiles(new FileFilter() { @Override public boolean accept(File path) { return path.isDirectory(); } });}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  13. 13. Avec une lambda ...private static File[] subDirectories(File directory) { FileFilter filter = (File path) -> path.isDirectory(); return file.listFiles(filter);}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  14. 14. Syntaxe pour les expressionssans paramètre () -> System.out.println("welcome to the land of shtis")avec un paramètre (inférence) employee -> employee.isManager()avec plusieurs paramètres en déclarant les types (int x, int y) -> x == y sans déclarer les types (inférence) (x, y) -> x == y
  15. 15. Syntaxe pour les instructionssans paramètre () -> { System.out.println("welcome to the land of shtis"); }avec un paramètre (inférence) employee -> { return employee.isManager(); }avec plusieurs paramètres (inférence) (index1, index2) -> { list.set(index1, list.get(index2)); }
  16. 16. SémantiqueTypé par une functional interface (ex SAM) Runnable, Callable, Filter, Function, ...Une lambda nest pas un objet “this” représente la classe courante pas la lambdaUne lambda est convertissable en un objet quiimplante une functional interface
  17. 17. En utilisant linférenceprivate static File[] subDirectories(File directory) { return file.listFiles( path -> path.isDirectory() );}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  18. 18. Comment linférence marche ?le compilo regarde le(s) type(s) target(s) FileFilter filter = path -> path.isDirectory();linterface FileFilter est déclarée interface FileFilter { boolean accept(File file); }utilise jamais le body de la lambda ! Java != Haskell
  19. 19. Method Referenceprivate static File[] subDirectories(File directory) { return file.listFiles( File::isDirectory );}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  20. 20. Method ReferenceRaccourçi si la lambda délègue juste à uneméthodeOn utilise :: rien que pour embéter les C++euxSi la méthode est surchargée, linférenceutilise le target type pour trouver les types desparamétres
  21. 21. Et les collectionsAvoir des lambdas cest bien, mais sanssupport au niveau des APIs ...Nouvelle interface java.util.Stream Sequentielle ou parallele Operations intermédiaire filter, map, sorted, distinct, flatMap ... terminales reduce, forEach, into, findFirst ...
  22. 22. Full Streamprivate static ArrayList<File> subDirectories( File directory) { return Arrays.stream(directory.listFiles()). filter(File::isDirectory). into(new ArrayList<>());}public static void main(String[] args) { File file = new File("."); for(File dir: subDirectories(file)) { System.out.println(dir); }}
  23. 23. Avec tout dans le main()La méthode subdirectories() sert pas vraiment !public static void main(String[] args) { File directory = new File("."); Arrays.stream(directory.listFiles()). filter(File::isDirectory). forEach(dir -> System.out.println(dir));}
  24. 24. Capturer la valeur de variables locales Et si je veux tous les sous-répertoires dont le nom commence par args[0] public static void main(String[] args) { File directory = new File("."); String name = args[0]; Arrays.stream(directory.listFiles()). filter(File::isDirectory). filter(dir -> dir.getName().startsWith(name). forEach(dir -> System.out.println(dir)); } Une lambda peut capturer les valeurs des variables locales (comme avec une classe anonyme)
  25. 25. Pipeline délementsArrays.asStream(array)collection.stream() filter filter blockiterator.stream() false false On pousse les élements à travers le pipeline Arrays.stream(directory.listFiles()). filter(File::isDirectory). filter(dir -> dir.getName().startsWith(name). forEach(dir -> System.out.println(dir));
  26. 26. Method reference sur une instanceIl est possible de créer une méthode réference sur uneinstancepublic static void main(String[] args) { File directory = new File("."); String name = args[0]; Arrays.stream(directory.listFiles()). filter(File::isDirectory). filter(path -> path.getName().startsWith(name). forEach(System.out::println);}
  27. 27. En résuméJava permet de définir des lambdas et desréférences sur des méthodesUne lambda est une expression ou une fonctionanonyme que lon peut convertir en un objetpour envoyer à une méthodeLAPI des collections est mis à jour poursupporter les lambdas
  28. 28. Partie IIChangements pour Java
  29. 29. Changements pour JavaGros problèmes Inférence déjà existante pas assez puissante Interface pas extensibleEt des petits + Effectively final Plus besoin de déclarer les variables locales utilisée dans les lambdas classes anonymes final Eviter les garbages classes Mettre les méthodes statiques public/private dans les interfaces
  30. 30. Améliorer linférenceJava 5/7 infére les variables de type des méthodes List<String> list =Array.asList("foo", "bar") List<String> list = Collections.emptyList(); les instantiations de types paramétrés List<String> list = new ArrayList<>(); => mais f(Collections.emptyList()) marche pas !
  31. 31. Inference avec Java 8Inference pour les types des lambdas donc de gauche à droite comme les <>Inference pour appel de méthode/instantiationdiamond (JEP 101) si dans un appel de méthode foo(new ArrayList<>()); // ok avec propagation String s = Collections.emptyList().get(0); // ok
  32. 32. Les demi-dieux de linférenceDan Smith Maurizio Cimadamore
  33. 33. Extensibilité des interfacesOn veux obtenir un Stream à partir duneCollection collection.stream()On ne peut pas ajouter une méthode dans uneinterface !Solution académique: traits (!= Scala traits)
  34. 34. TraitUn type contenant des méthodes abstraites etdes méthodes concrètes (mais pas de champs)Ajouter une méthode si lon fournit limplantationne casse pas la compatibilitéidée: et si on pouvait mettre du code dans uneinterface
  35. 35. but, you broke Java !
  36. 36. Default methodPermet de fournir un code par défaut qui est utiliser si il nennexiste pasinterface Iterator<T> { public boolean hasNext(); public T next(); public default void remove() { throw new UnsupportedOperationException(); } public default void forEach(Block<? super T> block) { while(hasNext()) { block.accept(it.next()); } }}
  37. 37. SémantiqueLa méthode par défaut nest utilisée que“par défaut”interface A { default void m() { ... }}class B implements A{ void m() { ... } // pas besoin de A::m !}
  38. 38. Héritage mutiple ??interface A { default void m() { ... }}interface B { default void m() { ... }}class C implements A, B { // compile pas, faut choisir A::m ou B::m }
  39. 39. Héritage mutipleinterface A { default void m() { ... }}interface B { default void m() { ... }}class C implements A, B { // on doit fournir un code pour m() public void m() { A.super.m(); // on appel m de A B.super.m(); // on appel m de B }}
  40. 40. Partie IIIdans les entrailles de la VM
  41. 41. Au menu ...Comment les méthodes par défautfonctionnent ?Comment les lambdas sont compilés ?Comment les lambdas sont optimisées par laVM ?
  42. 42. Méthode par défautLe compilo ne peut rien faire ! Sinon on doit recompiler toutes les librariesDoit être fait par la VM mais ● les règles de redéfinition (override) dépendent des generics ● Les générics sont un artifact à la compile pas connu à lexécution (erasure)La VM doit savoir lire les signatures des generics
  43. 43. Méthode par défaut et erasureinterface Foo<T> { default T getFoo() { return ...; }}interface Bar { String getFoo();}class A implements Bar, Foo<String> {}
  44. 44. Méthode par défaut et erasureinterface Foo<T> { default ObjectT getFoo() { return ...; }}interface Bar { String getFoo();}class A implements Bar, Foo<String> {} la VM doit générer deux méthodes Object getFoo() et String getFoo()
  45. 45. Méthode par défaut et erasureinterface Foo<T> { default ObjectT getFoo() { return ...; }}interface Bar { String getFoo();}class A implements Bar, Foo<String> { Object getFoo() { return getFoo(); // appel String getFoo() } String getFoo() { return ...; // recopie le bytecode de Foo::getFoo }}
  46. 46. Compiler une lambda naïvementOn créé une méthode synthetic pour le corps de la lambdaOn crée une classe anonyme lors de la convertion vers lafunctional interfaceiterator.forEach(dir -> System.out.println(dir));devientiterator.forEach(new Block<File>() { public void accept(File dir) { return lambda$1(dir); }});static void lambda$1(File dir) { System.out.println(dir);}
  47. 47. Lambda objet constant ?A lexécution, il y a deux sortes de lambdas Les lambdas qui ne captures pas de variable ou les méthodes référence sur une classe ● path -> path.isDirectory ● File::isDirectory Celles qui capture des variables ou les méthode référence sur des instances ● path -> path.getName().startsWith(args[0) ● System.out::println
  48. 48. Compiler vers une classe anonyme ? Et on se récupère tous les problèmes de perf des classes anonymes De plus, si une lambda ne capture pas de variable, on pourrait au moins la crée que une seule fois Mais si on utilise un champ static final, linitialisation à lieu même si on ne lutilise pas
  49. 49. Invokedynamic to rule them allOn veut un mécanisme qui délai linitialisation aupremier accès invokedynamicOn veut un mécanisme qui permet dindiquer que lerésultat dun calcul est constant invokedynamicOn veut un pointeur de fonction pour éviter lacréation des classe anonymes java.lang.invoke.MethodHandle
  50. 50. Compiler une lambdaiterator.forEach(dir -> System.out.println(dir));devientiterator.forEach(invokedynamic bootstrap [lambda$1]);static void lambda$1(File dir) { System.out.println(dir);}CallSite bootstrap(Lookup lookup, String name, MethodType type, MethodHandle mh) { if (type.parameterCount() == 0) { return new ConstantCallSite(proxy(mh)); } return ...}
  51. 51. Lambda ProxyInstance dune classe qui contient le pointeur defonction (MethodHandle) vers la lambda àexécuterIl ny a besoin que dune classe proxy parfunctional interfaceLe proxy est généré dynamiquement par la VM,pas forcément besoin de bytecode associé
  52. 52. Lambda Proxy en pseudo codeLe code Java correspondant est à peu près :public class Proxy$Block implements Block { private final @Stable MethodHandle mh; public void accept(Object o) { mh.invokeExact(o); }}invokeExact() est un pointer check + un appel à unpointeur de fonction
  53. 53. Optimizations lors de lappelAvoir 1 seul classe pour 1 interface permet à la VM deremplacer lappel à linterface par le code de la classe(Class Hierarchy Analysis)interface Iterator<T> { ... public default void forEach(Block<? super T> block) { while(hasNext()) { block.accept(it.next()); } }}ici, block est toujours une instance de Proxy$Block
  54. 54. Optimizations lors de lappelSi un objet constant dans une boucle, il est sortie de la boucleinterface Iterator<T> { ... public default void forEach(Block<? super T> block) { while(hasNext()) { mh.invokeExact(it.next()); } }}Dans le cas dun method handle, il faut inliner le coderéférencé par le pointer de fonction (fairy tale mode)
  55. 55. dans le monde merveilleux des licornesOn guarde la boucle avec un test sur le method handle(comme pour lOn Stack Replacement)interface Iterator<T> { ... public default void forEach(Block<? super T> block) { if (block.mh == lambda$1) { while(hasNext()) { System.out.println(it.next()); } } else deopt(); }}et comme block est pris en paramètre, lidée est de spécialiser le code àlendroit de lappel ce qui permet de ne pas faire le test
  56. 56. Le léger hicLe code de la VM qui crée le lambda proxy et qui loptimiseest pas prêt Le plan est prêt depuis longtemps, cest la réalisation qui prend du tempsOn doit quand même livrer un truc pour le JDK8Solution temporaire: la méthode de bootstrap génère uneclasse anonyme dynamiquement en utilisant ASM On implantera les lambda proxies dans une update du jdk8
  57. 57. En résuméLa première version des lambdas sera pas laplus optimiséeLAPI dans java.util doit être fini pour finjanvierLa spec de linférence pas fini même si ça sepréciseBref on est grâve à la bourre
  58. 58. Si vous avez du temps libreet même si vous nen avez pas !Downloader la dernière version du jdk8 avec leslambdas http://jdk8.java.net/lambda/Tester lAPI, jouer avec, remonter tous les bugs lambda-dev@openjdk.java.net

×