Lambdas: Myths and Mistakes

1,462 views
1,363 views

Published on

Java 8 will ship with a powerful new abstraction - Lambda Expressions (aka Closures) and a completely retooled set of Collections libraries. In addition interfaces have changed through the addition of default and static methods.

The ongoing debate as to whether Java should include such language changes has resulted in many vocal opinions being espoused. Sadly few of these opinions have been backed up by practical experimentation and experience.

* Are these opinions just myths?
* What mistakes does a developer make?
* Can a ‘blue collar’ Java Developer cope with functional programming?
* Can we avoid these mistakes in future?

In London we’ve been running a series of hackdays trying out Lambda Expressions as part of the Adopt-a-JSR program and have been recording and analysing the results. This gives us unprecedented insight into what will happen in future.

Published in: Technology, News & Politics
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,462
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
43
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Lambdas: Myths and Mistakes

  1. 1. Lambdas: Myths and Mistakes by Richard Warburton
  2. 2. Learning FP is so much more than just knowing what map/fold/filter/etc. do. It's more like getting good at Portal :) @copumpkin
  3. 3. Who am I? Currently work at jClarity Focus on performance diagnostics and static analysis Adopt a JSR (Date and Time + Lambdas) Writing a book for O'Reilly Media on Lambda Expressions in Java 8.
  4. 4. Talk Structure Background and Motivation Introduction to Lambda Expressions Beyond the Myths Functional Thinking
  5. 5. Background and Motvation
  6. 6. Lambda Expressions are coming in Java 8!
  7. 7. Lots of discussion/debate
  8. 8. How can we help? Adopt-a-JSR
  9. 9. Adopt-a-JSR? Community driven standards feedback Hackdays Serve on Expert Groups Initiated by Martijn Verburg
  10. 10. Some discussion unimportant Concrete Examples focus discussion
  11. 11. Introduction to Lambda Expressions
  12. 12. Overview of Lambdas Goal: Better Libraries Example: Data Parallelism Approach: Allow Code as Data
  13. 13. Action Listener button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("button clicked"); } });
  14. 14. Code as Data button.addActionListener( ? );
  15. 15. Need a parameter button.addActionListener(event );
  16. 16. Lambda Example button.addActionListener(event -> System.out.println("button clicked") );
  17. 17. No parameters Runnable helloWorld = () -> System.out.println("Hello World");
  18. 18. Variable Capture String name = getUserName(); button.addActionListener(event -> System.out.println("hi " + name) );
  19. 19. Functional Interfaces Everything in Java has a type Problem: Need types to represent Functions Solution: Use interfaces
  20. 20. Functional Interfaces public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent event); }
  21. 21. Functional Interfaces Want to support automated Data Parallelism Construct for building computation pipelines Iterator with inversion of control
  22. 22. External Iteration int count = 0; for (Artist artist : artists) { if (artist.isFrom("London")) { count++; } }
  23. 23. Internal Iteration artists.stream() .filter(artist -> artist.isFrom("London"));
  24. 24. map
  25. 25. map List<String> collected = Stream.of("a", "b", "hello") .map(string -> string.toUpperCase()) .collect(toList()); assertEquals(asList("A", "B", "HELLO"),collected);
  26. 26. reduce
  27. 27. reduce int count = Stream.of(1, 2, 3) .reduce(0, (acc, x) -> acc + 1); assertEquals(3, count);
  28. 28. filter
  29. 29. filter List<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1") .filter(value -> isDigit(value.charAt(0))) .collect(toList()); assertEquals(asList("1abc"), beginningWithNumbers);
  30. 30. Putting it all together for a given an album, find the nationality of every band playing on that album
  31. 31. Putting it all together (2) 1. transform an album into its artists 2. figure out which artists are bands 3. find the nationalities of each band
  32. 32. Putting it all together (3) List<String> origins = album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());
  33. 33. Method References str -> str.length String::length x -> foo.bar(x) foo::bar str -> new Name(str) Name::new
  34. 34. Beyond the Myths
  35. 35. Claim: Syntax is the most important thing about Lambda Expressions
  36. 36. Yeah, I liked the # syntax proposal better, too. One less character to type! :)
  37. 37. Have you considered 'default null'? It will save a keyword
  38. 38. How about a single punctuation mark, currently unused, as syntax sugar for "()- >".
  39. 39. (_, _) -> _ + _ This is starting to look like risque ASCII art :)
  40. 40. Its a Myth!
  41. 41. Claim: Syntax is irrelevant
  42. 42. // Originally invalid Stream.of(1, 2, 3) .forEach(x -> System.out.println(x)); // Required extra ; Stream.of(1, 2, 3) .forEach(x -> System.out.println(x););
  43. 43. Difference between expectations Many language features stolen! adapted Missing Features Stronger Type System Tuples List construction syntax
  44. 44. Framing Effect Different reactions depending on whether something is presented as a loss or a gain.
  45. 45. Recall our earlier example List<String> origins = album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());
  46. 46. Eager vs Lazy (2) album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality())
  47. 47. Eager vs Lazy (3) album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .collect(toList());
  48. 48. Very little TDD Only a couple of developers used TDD or unit tests Maybe a reflection on general TDD Maybe unfamiliarity with testing functional code
  49. 49. How do I test this? list.stream() .map(x -> 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(0, x))) .collect(toList());
  50. 50. Approach 1: Test surrounding function Don't test the lambda Test the method its surrounded by Works well for simple lambdas
  51. 51. Approach 2: Extract Method double complexFunction(double x) { return 1.0 / Math.ceil(1 + Math.pow(x) + Math.atan2(0, x)); } list.stream() .map(this::complexFunction) .collect(toList());
  52. 52. Mistake: debugging list.stream() .filter(filteringFunction) .map(mappingFunction) .collect(toList()); List<Bar> bars = new ArrayList<>(); for (Foo element : list) { if (filteringFunction(element) { Bar result = mappingFunction(element); bars.add(result); } }
  53. 53. peek list.stream() .filter(filteringFunction) .peek(e -> System.out.println("Filtered value: " + e)); .map(mappingFunction) .map(e -> e); .collect(toList());
  54. 54. Compiler Error Messages
  55. 55. Comparators Comparator<String> comparator = comparing(String::length); Comparator<String> comparator = comparing(str -> str.length);
  56. 56. Compiler Error java: reference to comparing is ambiguous both method <T>comparing(java.util.function.ToIntFunction< ? super T>) in java.util.Comparator and method <T,U>comparing(java.util.function.Function< ? super T,? extends U>) in java.util.Comparator match
  57. 57. What happened? // Generic object variant public static <T, U extends Comparable< ? super U>> Comparator<T> comparing(Function< ? super T, ? extends U> keyExtractor) // Specialised primitive variant public static <T> Comparator<T> comparing(ToIntFunction< ? super T> keyExtractor)
  58. 58. Summary Syntax important, but not in the way people think New approaches for debugging and testing Take care of overloads and compiler error messages
  59. 59. Functional Thinking
  60. 60. Functional Thinking?Thinking in terms of the input to output relationship and not a sequence of steps
  61. 61. First code that people write List<Integer> numbers = Arrays.asList(1, 2, 3); numbers.forEach(x -> { System.out.println(x); });
  62. 62. Non-idiomatic Proposals Eg: capture non-final local variables
  63. 63. Example Problem Count the number of instances of each word in a document.
  64. 64. Example Problem
  65. 65. Ideal Solution reader.lines() .flatMap(s -> Stream.of(s.split(" "))) .collect(groupingBy(s -> s, counting()));
  66. 66. Ideal Solution (then) reader.lines() .flatMap(s -> Stream.of(s.split(" "))) .collect(groupingBy(s -> s, reducing(s -> 1, Integer::sum))); // Map entries for "dad" // [ "dad", "dad", "dad" ] => [1, 1, 1] => 3
  67. 67. Bad Solution (Part 1) Map<string, list<string="">> initial = br.lines() .flatMap(s -> Arrays.stream(s.split(" "))) .collect(groupingBy(s -> s)); Map<map.entry<string, integer="">, Integer> freq1 = initial .entrySet().stream() .map(entry -> new AbstractMap.SimpleImmutableEntry<string, integer="">(entry.ge .collect(Collectors.toMap(entry -> entry.getValue())); </string,></map.entry<string,></string,>
  68. 68. Bad Solution (Part 2) Supplier<hashmap<string, integer="">> supplier = () -> new HashMap<string, integer="">(); BiConsumer<hashmap<string, integer="">, Map.Entry<string, integer="">> accum = (HashMap<string, integer=""> result, Map.Entry<string, integer=""> entry) -> result.put(entry.getKey(), entry.getValue()); BiConsumer<hashmap<string, integer="">, HashMap<string, integer="">> merger = HashMap::putAll; Map<string, integer=""> freq2 = initial.entrySet().stream() .map(entry -> new AbstractMap.SimpleImmutableEntry<string, integer="">(entry.ge .collect(supplier, accum, merger); </string,></string,></string,></hashmap<string,></string,></str
  69. 69. Summary Functional Thinking the most important thing to learn Not Java specific at all Requires Practise
  70. 70. Conclusions Gone through a bunch of examples of specific issues ‘Functional Thinking’: Not necessary to start learning. Try before you buy
  71. 71. Questions?@RichardWarburto http://insightfullogic.com https://github.com/RichardWarburton/

×