OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

44
Simone Bordet Mario Fusco OOP and FP Become a Better Programmer

Transcript of OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Page 1: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

OOP and FP

Become aBetter Programmer

Page 2: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Our definitionOf OOP and FP

Page 3: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 4: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 5: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Goals

Page 6: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 7: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #1“accumulator”

Page 8: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #1, v1

public String sum(List<Student> students) {

StringBuilder sb = new StringBuilder();

for (Student s : students)

sb.append(s.getName()).append(“, “);

return sb.toString();

}

OOP style External iteration Mutable variables

Page 9: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #1, v2

public String sum(List<Student> students) {

StringBuilder sb = new StringBuilder();

students.stream()

.forEach(s -> sb.append(s.getName()).append(“, “));

return sb.toString();

}

BAD style Use of mutable accumulator

Page 10: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #1, v3

public String sum(List<Student> 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

Page 11: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #2“what do you do ?”

Page 12: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #2, v1

What does this code do ?

List<Student> students = ...;

int min = 0;

for (Student s : students) {

if (s.getGradYear() != 2014)

continue;

int score = s.getGradScore();

if (score > min)

min = score;

}

Page 13: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #2, v2

Calculates the max, not the min ! And only for 2014 students !

List<Student> 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

Page 14: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #2, v3

But how do you get 2 results iterating once ?

List<Student> 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

Page 15: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #2, v4

FP version:

Pair<Integer, Integer> 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 !?!

Page 16: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #2, v5

How about parallelizing this ?

Pair<Integer, Integer> 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.

Page 17: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #3“let's group them”

Page 18: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #3, v1

Group students by their graduation year

Map<Integer, List<Student>> studentByGradYear = new HashMap<>();

for (Student student : students) {

int year = student.getGradYear();

List<Student> list = studentByGradYear.get(year);

if (list == null) {

list = new ArrayList<>();

studentByGradYear.put(year, list);

}

list.add(student);

}

Page 19: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #3, v2

Map<Integer, List<Student>> studentByGradYear =

students.stream()

.collect(groupingBy(student::getGradYear));

Page 20: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #4“separation of concerns”

Page 21: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #4, v1

Read first 40 error lines from a log file

List<String> 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();

}

Page 22: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #4, v2

List<String> errors = Files.lines(Paths.get(fileName))

.filter(l -> l.startsWith("ERROR"))

.limit(40)

.collect(toList());

Page 23: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #4

List<String> 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());

Page 24: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #5“grep -B 1”

Page 25: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #5, v1

Find lines starting with “ERROR” and previous line

List<String> 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();

}

Page 26: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 27: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #5, v2

Files.lines(Paths.get(filename))

.reduce(new LinkedList<String[]>(),

(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());

Page 28: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #6“callback hell”

Page 29: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #6, v1

Find a term, in parallel, on many search engines, then execute an action

final List<SearchEngineResult> 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);

});

}

Page 30: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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 !

Page 31: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #6, v2

CompletableFuture<List<SearchEngineResult>> result =

CompletableFuture.completed(new CopyOnWriteArrayList<>());

for (Engine e : engines) {

CompletableFuture<Response> 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));

Page 32: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Example #6, v3

List<CompletableFuture<SearchEngineResult>> 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));

Page 33: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #7“statefulness”

Page 34: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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 {

}

Page 35: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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;

}

Page 36: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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 { }

Page 37: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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<Cat, Bird, CatWithPrey> chase = Cat::chase;

BiFunction<Cat, Bird, FullCat> useCase =

chase.andThen(CatWithPrey::eat);

More classes, but clearer semantic

Page 38: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Example #8“encapsulation”

Page 39: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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.

}

}

Page 40: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 41: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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

Page 42: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Conclusions

Page 43: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

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.

Page 44: OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemotion Rome 2015

Simone Bordet

Mario Fusco

Questions&

Answers