Slide 1

Slide 1 text

Simone Bordet Mario Fusco OOP and FP Become a Better Programmer

Slide 2

Slide 2 text

Our definition Of OOP and FP

Slide 3

Slide 3 text

Simone Bordet Mario Fusco Our Definition of OOP  In this presentation “OOP” will mean:  Idiomatic Java 7 programming style:  Use of mutable variables and state  Use of classes and void methods  Use of external iteration (for loops)  Use of threads

Slide 4

Slide 4 text

Simone Bordet Mario Fusco Our definition of FP  In this presentation “FP” will mean:  Java 8 programming style, with:  Immutable variables and state  Use classes, but avoid void methods  Internal iteration via Stream  CompletableFuture, not Thread

Slide 5

Slide 5 text

Goals

Slide 6

Slide 6 text

Simone Bordet Mario Fusco Goals  This session is about OOP and FP  NOT about OOP versus FP  We want to show that in order to be a better programmer, you have to know both  In some cases it's better to apply one paradigm  In other cases it's better to apply the other  We will hint at some guideline that helps deciding

Slide 7

Slide 7 text

Example #1 “accumulator”

Slide 8

Slide 8 text

Simone Bordet Mario Fusco Example #1, v1 public String sum(List students) { StringBuilder sb = new StringBuilder(); for (Student s : students) sb.append(s.getName()).append(“, “); return sb.toString(); }  OOP style  External iteration  Mutable variables

Slide 9

Slide 9 text

Simone Bordet Mario Fusco Example #1, v2 public String sum(List students) { StringBuilder sb = new StringBuilder(); students.stream() .forEach(s -> sb.append(s.getName()).append(“, “)); return sb.toString(); }  BAD style  Use of mutable accumulator

Slide 10

Slide 10 text

Simone Bordet Mario Fusco Example #1, v3 public String sum(List students) { String names = students.stream() .map(s -> s.getName() + “, “) .reduce(“”, (a, b) -> a + b); return names; }  FP style  Internal iteration  Mapping input to output  No mutable variables

Slide 11

Slide 11 text

Example #2 “what do you do ?”

Slide 12

Slide 12 text

Simone Bordet Mario Fusco Example #2, v1  What does this code do ? List students = ...; int min = 0; for (Student s : students) { if (s.getGradYear() != 2014) continue; int score = s.getGradScore(); if (score > min) min = score; }

Slide 13

Slide 13 text

Simone Bordet Mario Fusco Example #2, v2  Calculates the max, not the min !  And only for 2014 students ! List students = ...; students.stream() .filter(s -> s.getGradYear() == 2014) .mapToInt(Student::getScore) .max();  Somehow clearer to read  Less possibility of mistakes  max() is a method, not a variable name

Slide 14

Slide 14 text

Simone Bordet Mario Fusco Example #2, v3  But how do you get 2 results iterating once ? List students = ...; int min = Integer.MAX_VALUE, max = 0; for (Student s : students) { int score = s.getGradScore(); min = Math.min(min, score); max = Math.max(max, score); }  Easy and readable

Slide 15

Slide 15 text

Simone Bordet Mario Fusco Example #2, v4  FP version:  Pair result = students.stream() .map(s -> new Pair<>(s.getScore(), s.getScore())) .reduce(new Pair<>(Integer.MAX_VALUE, 0), (acc,elem)-> { new Pair<>(Math.min(acc._1, elem._1), Math.max(acc._2, elem._2)) });  What !?!

Slide 16

Slide 16 text

Simone Bordet Mario Fusco Example #2, v5  How about parallelizing this ? Pair result = students.stream().parallel() .map(s -> new Pair<>(s.getScore(), s.getScore())) .reduce(new Pair<>(Integer.MAX_VALUE, 0), (acc,elem)-> { new Pair<>(Math.min(acc._1, elem._1), Math.max(acc._2, elem._2)) });  Neat, but .parallel() can only be used under very strict conditions.

Slide 17

Slide 17 text

Example #3 “let's group them”

Slide 18

Slide 18 text

Simone Bordet Mario Fusco Example #3, v1  Group students by their graduation year Map> studentByGradYear = new HashMap<>(); for (Student student : students) { int year = student.getGradYear(); List list = studentByGradYear.get(year); if (list == null) { list = new ArrayList<>(); studentByGradYear.put(year, list); } list.add(student); }

Slide 19

Slide 19 text

Simone Bordet Mario Fusco Example #3, v2 Map> studentByGradYear = students.stream() .collect(groupingBy(student::getGradYear));

Slide 20

Slide 20 text

Example #4 “separation of concerns”

Slide 21

Slide 21 text

Simone Bordet Mario Fusco Example #4, v1  Read first 40 error lines from a log file List errorLines = new ArrayList<>(); int errorCount = 0; BufferedReader file = new BufferedReader(...); String line = file.readLine(); while (errorCount < 40 && line != null) { if (line.startsWith("ERROR")) { errorLines.add(line); errorCount++; } line = file.readLine(); }

Slide 22

Slide 22 text

Simone Bordet Mario Fusco Example #4, v2 List errors = Files.lines(Paths.get(fileName)) .filter(l -> l.startsWith("ERROR")) .limit(40) .collect(toList());

Slide 23

Slide 23 text

Simone Bordet Mario Fusco Example #4 List errorLines = new ArrayList<>(); int errorCount = 0; BufferedReader file = new BufferedReader(new FileReader(filename)); String line = file.readLine(); while (errorCount < 40 && line != null) { if (line.startsWith("ERROR")) { errorLines.add(line); errorCount++; } line = file.readLine(); } return errorLines; return Files.lines(Paths.get(fileName)) .filter(l -> l.startsWith("ERROR") .limit(40) .collect(toList());

Slide 24

Slide 24 text

Example #5 “grep -B 1”

Slide 25

Slide 25 text

Simone Bordet Mario Fusco Example #5, v1  Find lines starting with “ERROR” and previous line List errorLines = new ArrayList<>(); String previous = null; String current = reader.readLine(); while (current != null) { if (current.startsWith("ERROR")) { if (previous != null) errorLines.add(previous); errorLines.add(current); } previous = current; current = reader.readLine(); }

Slide 26

Slide 26 text

Simone Bordet Mario Fusco Example #5, v2  Not easy – immutability is now an obstacle  Must read the whole file in memory  This does not work: Stream.generate(() -> reader.readLine())  readLine() throws and can't be used in lambdas

Slide 27

Slide 27 text

Simone Bordet Mario Fusco Example #5, v2 Files.lines(Paths.get(filename)) .reduce(new LinkedList(), (list, line) -> { if (!list.isEmpty()) list.getLast()[1] = line; list.offer(new String[]{line, null}); return list; }, (l1, l2) -> { l1.getLast()[1] = l2.getFirst()[0]; l1.addAll(l2); return l1; }).stream() .filter(ss -> ss[1] != null && ss[1].startsWith("ERROR")) .collect(Collectors.toList());

Slide 28

Slide 28 text

Example #6 “callback hell”

Slide 29

Slide 29 text

Simone Bordet Mario Fusco Example #6, v1  Find a term, in parallel, on many search engines, then execute an action final List result = new CopyOnWriteArrayList<>(); final AtomicInteger count = new AtomicInteger(engines.size()); for (Engine e : engines) { http.newRequest(e.url("codemotion")).send(r -> { String c = r.getResponse().getContentAsString(); result.add(e.parse(c)); boolean finished = count.decrementAndGet() == 0; if (finished) lastAction.perform(result); }); }

Slide 30

Slide 30 text

Simone Bordet Mario Fusco Example #6, v1  Code smells  Mutable concurrent accumulators: result and count  Running the last action within the response callback  What if http.newRequest() returns a CompletableFuture ?  Then I would be able to compose those futures !  Let's try to write it !

Slide 31

Slide 31 text

Simone Bordet Mario Fusco Example #6, v2 CompletableFuture> result = CompletableFuture.completed(new CopyOnWriteArrayList<>()); for (Engine e : engines) { CompletableFuture request = http.sendRequest(e.url("codemotion")); result = result.thenCombine(request, (list, response) -> { String c = response.getContentAsString(); list.add(e.parse(c)); return list; }); } result.thenAccept(list -> lastAction.perform(list));

Slide 32

Slide 32 text

Simone Bordet Mario Fusco Example #6, v3 List> results = engines.stream() .map(e -> new Pair<>(e, http.newRequest(e.url("codemotion")))) .map(p -> p._2.thenCombine(response -> p._1.parse(response.getContentAsString()))) .collect(toList()); CompletableFuture.supplyAsync(() -> results.stream() .map(future -> future.join()) .collect(toList())) .thenApply(list -> lastAction.perform(list));

Slide 33

Slide 33 text

Example #7 “statefulness”

Slide 34

Slide 34 text

Simone Bordet Mario Fusco Example #7, v1 class Cat { private Bird prey; private boolean full; void chase(Bird bird) { prey = bird; } void eat() { prey = null; full = true; } boolean isFull() { return full; } } class Bird { }

Slide 35

Slide 35 text

Simone Bordet Mario Fusco Example #7, v1  It is not evident how to use it: new Cat().eat() ???  The use case is instead: Cat useCase(Cat cat, Bird bird) { cat.chase(bird); cat.eat(); assert cat.isFull(); return cat; }

Slide 36

Slide 36 text

Simone Bordet Mario Fusco Example #7, v2  How about we use types to indicate state ? class Cat { CatWithPrey chase(Bird bird) { return new CatWithPrey(bird); } } class CatWithPrey { private final Bird prey; public CatWithPrey(Bird bird) { prey = bird; } FullCat eat() { return new FullCat(); } } class FullCat { }

Slide 37

Slide 37 text

Simone Bordet Mario Fusco Example #7, v2  Now it is evident how to use it: FullCat useCase(Cat cat, Bird bird) { return cat.chase(bird).eat(); } BiFunction chase = Cat::chase; BiFunction useCase = chase.andThen(CatWithPrey::eat);  More classes, but clearer semantic

Slide 38

Slide 38 text

Example #8 “encapsulation”

Slide 39

Slide 39 text

Simone Bordet Mario Fusco Example #8, v1 interface Shape2D { Shape2D move(int deltax, int deltay) } class Circle implements Shape { private final Point center; private final int radius; Circle move(int deltax, int deltay) { // translate the center } } class Polygon implements Shape { private final Point[] points; Polygon move(int deltax, int deltay) { // Translate each point. } }

Slide 40

Slide 40 text

Simone Bordet Mario Fusco Example #8, v1 for (Shape shape : shapes) shape.move(1, 2);  How do you do this using an FP language ?  What is needed is dynamic polymorphism  Some FP language does not have it  Other FP languages mix-in OOP features

Slide 41

Slide 41 text

Simone Bordet Mario Fusco Example #8, v2 defn move [shape, deltax, deltay] ( // Must crack open shape, then // figure out what kind of shape is // and then translate only the points )  OOP used correctly provides encapsulation  FP must rely on OOP features to provide the same  Data types are not enough  Pattern matching is not enough  Really need dynamic polimorphism

Slide 42

Slide 42 text

Conclusions

Slide 43

Slide 43 text

Simone Bordet Mario Fusco Conclusions  If you come from an OOP background  Study FP  If you come from an FP background  Study OOP  Poly-paradigm programming is more generic, powerful and effective than polyglot programming.

Slide 44

Slide 44 text

Simone Bordet Mario Fusco Questions & Answers