Upload
yanai-franchi
View
1.402
Download
3
Tags:
Embed Size (px)
DESCRIPTION
The slides from Tikal Meetup - http://www.meetup.com/full-stack-developer-il/events/188639342/
Citation preview
1
Ready for "Functional Programming"
with Java 8 ?
Yanai Franchi , Tikal
2
Agenda
● First-Class Functions● FP with Streams● Working Concurrency● Demo
3
First Class Functions
4
Our Inventory = List of Apples
5
Class Apple
● getColor() : String● getWeight() : int
6
Green Apples, Please...
7
Green Apples, Please...
8
Filter Green Apples
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getColor().equals(“green”)) {
result.add(apple);
}
}
return result;
}
Starts empty, add green apples
one by one
Select only green apples
9
Now Heavy Ones , Please...
10
Easy...We Copy&Paste :(
public static List<Apple> filterHeavyApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
We only change the predicate
11
What's the Difference ?
public static List<Apple> filterGreenApples
(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getColor.equals(“green”)) {
result.add(apple);
}
}
return result;
}
public static List<Apple> filterHeavyApples
(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
12
13
New Behavior
BehaviorParameterization
Output
apple.getWeight > 150 apple.getColor.equals(“green”)
Heavy Apples Green Apples
static List<Apple> filterApples(
List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
14
In Practice, Our Client...Yuck :(
List<Apple> greenApples =
filterApples(inventory,new Predicate<Apple>() {
@Override
public boolean test(Apple a) {
return a.getColor().equals(“green”);
}
});
A lot of “noise”
15
16
Java8 Lambda to Rescue
filterApples(inventory,
(Apple a) → {return a.getColor().equals(“green”)});
17
Sending Lambda Expression
filterApples(inventory,
(Apple a) → a.getColor().equals(“green”));
18
Making it Shorter
filterApples(inventory,
(a) → a.getColor().equals(“green”));
Apple Type is Inferred
19
...Even Shorter
filterApples(inventory,
a → a.getColor().equals(“green”));
20
“Capture” Values (a.k.a “Closure”)
String color = “green”;
filterApples(inventory,
a → a.getColor().equals(color));
Implicitly final
21
22
Anonymous Function that Can Be Passed Around
filterApples(inventory,
a → a.equals.getColor(“green”));
23
Switch to Method Reference
filterApples(inventory,a → a.equals.getColor(“green”));
24
Switch to Method Reference
filterApples(inventory,Apple::isGreen);
filterApples(inventory,a → a.getColor().equals(“green”));
isGreen is declared in Apple
25
Until Now...
26
...In Java 8
27
Lambda Type = Functional Interface
Predicate<Apple> redApple = a → a.getColor().equals(“red”);
Predicate is the type of our lambda
28
Meet Functional Interface
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
Just Marker
● Definition: a functional interface is an interface with one abstract method
● Single Abstract Method (SAM) type
SAM
29
Default Methods Implementations in Interface
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
default Predicate<T> negate(){
return (t) -> !test(t);
}
default or(Predicate<? super T> other){
return (t) -> test(t) ||
other.test(t);
}
default and(Predicate<? super T> other){
return (t) -> test(t) &&
other.test(t);
}
}
Default Method
30
Existing Function Interfaces
interface Comparator<T> { boolean compare(T x, T y); }
(int x, int y) → x - y ;
interface FileFilter { boolean accept(File x); }
f → f.isDirectory()
interface Runnable { void run(); }
() → someCode();
interface ActionListener{ void actionPerformed(ActionEvent ae); }
(ae -> log.debug(“Got it!”));
31
New Function Interfaces
● Predicate<T> : boolean test(T t);
– Determine if the input of type T matches some criteria : ● Consumer<T> : void accept(T t);
– Accept a single input argument of type T, and return no result :
● Supplier<T> : T get();
– A factory that’s expected to return either a new instance or a pre-created instance :
● Function<T, R> : R apply(T t);
– Apply a function to the input type T, generating a result of type R
32
33
Sorting with Anonymous Class
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
High OrderFunction
34
Sort with Lambda
inventory.sort( (a1,a2) → a1.getWeight().compareTo(a2.getWeight()));
35
Using “comparing” Method
Comparator InterfaceStatic method
inventory.sort(comparing(Apple::getWeight));
Static method that accept Function and return Comparator
36
FP with Streams
37
Dish
Row 1 Row 2 Row 3 Row 40
2
4
6
8
10
12
Column 1
Column 2
Column 3
● String getName()● boolean isVegeterian()● int getCalories()● Type getType()● List<String> getNickNames()● CaloricLevel getCaloricLevel()
38
Three Fat Dishes - Imperative
● Client controls iteration
● Inherently serial: iterate from beginning to end
● Not thread-safe because business logic is stateful (mutable accumulator variable)
● Frustratingly imperative
List<String> fatsDishes = ...
int count = 0;
for(Dish d : menu){
if (count > 3)
break;
if(d.getCalories > 300){
count++;
fatsDishes.add(d.getName());
}
}
External Iteration
39
40
FP → Declarative CodeList<String> fatDishes =
menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(toList());
41
42
But What is a Stream ?
● A sequence of elements from a source that supports aggregate operations– Aimed for computations and aggregations (In
contrary to collections which are data structures)
– Consume from a data-providing source such as Collections, Arrays, or IO resources.
– Support SQL-like operations and common operations from functional programing languages such as filter, map, reduce, find, match, sorted etc.
43
Creating Stream
● IntStream.range(1, 100)
● Stream.of("Java 8 ", "Lambdas");
● File.list(Paths.get("some-folder"))● Files.lines(Paths.get("data.txt"))● myList.parallelStream()
● Create infinite stream– Iterate
● Stream.iterate(0, n -> n + 2)● Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]})
– Stream.generate(Math::random)
44
High Order Function - map
● <R> Stream<R> map(Function<? super T, ? extends R> mapper);
● Apply the mapper function on each element of the stream , and create a new stream from its outputs by placing the results of the mapper on the new stream
● map(Dish::getName). Converts a Stream of dishes to Stream names (Strings), by applying Dish.getName() on the elements.
45
High Order Function - flatMap
● <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
● Apply the mapper function on each element of the stream and create a new stream from its outputs, by placing contents of the mapped streams in the new stream
● menu.stream().flatMap(d → d.getNickNames().stream())
– Converts a Stream of dishes to Stream of nickNames (String), by replacing all nicknames as elements of the stream.
46
Limit - “Short Circuit”List<String> names = menu.stream()
.filter(d -> {System.out.println("filtering" + d);
return d.getCalories() > 300;})
.map(d -> {System.out.println("mapping" + d);
return d.getName();})
.limit(3)
.collect(toList());
filtering pork
mapping pork
filtering beef
filtering chicken
mapping chicken
filtering eggfiltering spaghetti
mapping spaghetti
Short Circuit:Stopped printing
after 3 dishes
47
Finding & Matching - “Short Circuit”
● No need to process the whole stream● As soon as an element is found a result can be
produced.● Finding Operations:
– anyMatch, noneMatch, findFirst and findAny
boolean found =
menu.stream().anyMatch(Dish::isVegatarian)
48
Group Dishes by Type - Imperative
Map<Dish.Type, List<Dish>> groupDishesByTypeMap =
new HashMap<>();
for (Dish dish : menu) {
final Type type = dish.getType();
List<Dish> dishesForType = groupDishesByTypeMap.get(type);
if(dishesForType == null){
dishesForType = new ArrayList<>();
groupDishesByTypeMap.put(type, dishesForType);
}
dishesForType.add(dish);
}
return groupDishesByTypeMap;
49
Grouping with FP - Declarative
Map<Dish.Type, List<Dish>> groupDishesByTypeMap =
menu.stream().collect(groupingBy(Dish::getType));
Collectors static method
50
Count Dishes Per Type
Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType,counting()));
{MEAT=3, FISH=2, OTHER=4}Output
Collectors static method
51
One More Level...
52
One More Level...Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishesByTypeAndCaloricLevel = menu.stream() .collect(groupingBy( Dish::getType, groupingBy(Dish::getCaloricLevel)));
{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]},FISH={DIET=[prawns], NORMAL=[salmon]},OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}
}
Output
53
Working Concurrency
54
Process Dishes in Parallel
● Uses the ForkJoin framework behind the scenes● By Default number of threads is as the number of
processors● Will NOT keep the original order of the stream
menu.parallelStream().forEach(Dish::heavyCalculation);
55
Total Calories in Menu – Imperative
int i=0;for (Dish dish : menu) { i += dish.getCalories();}
Side Effect
56
We Know What To Do...
● The only difference is the reduction function● For min, max, avg etc – The code will look the
same
int i=0;for (Dish dish : menu) { i += dish.getCalories();}
int i=0;for (Dish dish : menu) { i = Math.max(dish.getCalories(),i);}
57
Move To FP – Bad Way :(
Accumulator accumulator = new Accumulator();menu.stream()
.map(Dish::getCalories)
.forEach(accumulator::add);accumulator.getTotal();
class Accumulator { private long total = 0;
long getTotal(){return total);
public void add(long value) { total += value; } } Side Effect
58
Break Concurrency!!! :(
Accumulator accumulator = new Accumulator();menu.parallelStream()
.map(Dish::getCalories)
.forEach(accumulator::add)accumulator.getTotal();
Different resultFor the same input
59
Reducing
Row 1 Row 2 Row 3 Row 40
2
4
6
8
10
12
Column 1
Column 2
Column 3
menu.parallelStream().map(Dish::getCalories).sum();
menu.parallelStream().map(Dish::getCalories).reduce(0,Integer:sum);
=
60
FP - Data is Immutable
● Once object is created, it can't be changed.● If you need to change an object, make a copy.
– Enables concurrency
– Rollback of data
– Simplicity
61
Mutable Tree – What's Wrong ?class Tree {
private String key;private int val;private Tree left, right;public Tree(String k, int v, Tree l, Tree r) {
key = k; val = v; left = l; right = r;}
}
public static Tree update(String k, int newval, Tree t) {if (t == null)
t = new Tree(k, newval, null, null);else if (k.equals(t.key))
t.val = newval;else if (k.compareTo(t.key) < 0)
t.left = update(k, newval, t.left);else
t.right = update(k, newval, t.right);return t;
}
62
Concurrency Problems
● Every user wants to share the identical data structure and see updates caused by any part of the program.
● Hence it is vital (but often overlooked) in nonfunctional code that whenever we add some form of structured value to a tree then we copy it - Who knows, someone may later assume they can update it.
● In Functional approach – Share storage for common parts of structure.
63
Marry22
Emily20 Titan
29
Alan50
Georgie23
Raoul25
Persistent Tree
64
Marry22
Emily20 Titan
29
Alan50
Georgie23
Raoul25
Input : “will”, 26
Persistent Tree
65
Marry22
Emily20 Titan
29
Alan50
Georgie23
Raoul25
Marry22
Titan29
Will26
Output of updateInput : “will”, 26
Persistent Tree
66
Persistent Treeclass Tree {
private String key;private int val;private Tree left, right;public Tree(String k, int v, Tree l, Tree r) {
key = k; val = v; left = l; right = r;}
}
public static Tree fupdate(String k, int newval, Tree t) { return (t == null) ? new Tree(k, newval, null, null) : k.equals(t.key) ? new Tree(k, newval, t.left, t.right) : k.compareTo(t.key) < 0 ? new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) : new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right));}
67
Analyze Multiple Apache Logs
68
Analyze Multiple Apache Logs
● Host, Time, Request, Response, Resp-Length199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...
199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...
199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...
69
Requirements
● Answer the following:– Are there any errors ?
– Are there any empty-body responses ?
– Find last 5 error
– Count statuses for each day
● Assumptions– Analyze multiple logs in “logs” folder
– Logs files are big and can't fit into memory
70
Design
● LogEntry - Entity Class– parse(String line) : LogEntry
● LogAnalyticService – Service Class– streamLogs() : Stream<LogEntry>
● Stream log files in folder● Per Each file Stream lines● Map each line to LogEntry
● anyMatch – Reuse for different predicates● groupBy – For grouping
71
Implementationprivate Stream<LogEntry> streamLogs() {
try {
return Files.list(Paths.get(loggingDir)).filter(p -> p.getFileName().endsWith(LOG_EXT)).flatMap(Files::lines).map(LogEntry::parse);
} catch (final Exception e) {throw new RuntimeException(e);
}}
Will not Compile
72
private Stream<LogEntry> streamLogs() {try {
return Files.list(Paths.get(loggingDir)).filter(p -> p.getFileName().endsWith(LOG_EXT)).flatMap(this::lines).map(LogEntry::parse);
} catch (final Exception e) {throw new RuntimeException(e);
}}
private Stream<String> lines(final Path path) {try {
return Files.lines(path);} catch (IOException e) {
logger.error("Failed to process "+path,e);return Stream.empty();
}}
73
Last Err Logs
return streamLogs() .filter((le) -> le.getResponse() >= 500) .sorted(comparing(LogEntry::getDateTime).reversed()) .limit(5) .collect(toList());
74
Sending Predicates
public boolean isAnyWithResponse(int response) {return anyMatch((le) -> le.getResponse() == response);
}
public boolean isAnyEmptyResponse() {return anyMatch((le) -> le.getByteSent() == 0);
}
boolean anyMatch(Predicate<? super LogEntry> predicate) {return streamLogs().anyMatch(predicate);
}
75
Grouping Results
Map<LocalDate, Map<Integer, Long>> groupingByDatesThenResponse() {return
streamLogs().collect(groupingBy(
LogEntry::getDate,TreeMap::new, groupingBy(LogEntry::getResponse, counting()))
);}
Ordered Keys
Nested Grouping
76
Let's Run it...
77
FP Summary
● Functions are first-class citizens– Can be declared as variables, pass as arguments
or be returned by other functions
● Declarative instead of imperative– We tell what to do, instead of how
● Data Immutability– Reduce Side Effect
– Easier path to parallel code
● Java 8 Lambda & Streams enable FP style
78
Thank you