IS JAVA 8 A TRUE FUNCTIONAL PROGRAMMING LANGUAGE ?
- Samir Chekkal
- Saad El Khlifi
1. Programming Paradigm Overview
2. Imperative Pardigm Vs Functional Paradigm
3. Corner Stones of Functional Paradigm
1. Immutablity
2. Recursion
3. Functions as first class citizens
4. Higher order functions
5. Lazyness
4. Conclusion
Plan
- A programming paradigm is a general approach, orientation, or philosophy
of programming that can be used when implementing a program.
- Main programming paradigms:
- Imperative programming: C, Java, Scala ...
- Functionnal programming: Haskell, ML, Scala, Java 8 (?) ...
- Logic programming: Prolog ...
- Object Oriented Programming: Java, Scala, ...
1.Programming Paradigm Overview
static List<String> approvisionnement() {
List<String> refs = new ArrayList<>();
int index = 0;
for(Product p : Store.catalog()) {
if(p.getCategory() == Product.Category.MOBILE_PHONE) {
refs.add(p.getRef());
index ++;
if(index > 10)
break;
}
}
Collections.sort(refs);
return refs;
}
2.Imperative Pardigm Vs Functional Paradigm
static Map<Product.Category, List<Product>> categories() {
Map<Product.Category, List<Product>> result = new HashMap<>();
for(Product p : Store.catalog()) {
if(result.get(p.getCategory()) != null) {
result.get(p.getCategory()).add(p);
} else {
List<String> catProducts = new ArrayList<>();
catProducts.add(p);
result.put(p.getCategory(), catProducts);
}
}
return result;
}
2.Imperative Pardigm Vs Functional Paradigm
static List<String> approvisionnement() {
return Store.catalog()
.stream()
.filter(p -> p.getCategory() == Product.Category.MOBILE_PHONE)
.limit(10)
.map(Product::getRef)
.sorted()
.collect(toList());
}
2.Imperative Pardigm Vs Functional Paradigm
static Map<Product.Category, List<Product>> categories() {
return Store.catalog()
.stream()
.collect(groupingBy(Product::getCategory));
}
static Map<Product.Category, List<String>> categories() {
return Store.catalog()
.stream()
.collect(groupingBy(Product::getCategory,
mapping(Product::getRef, toList())));
}
2.Imperative Pardigm Vs Functional Paradigm
3. Corner Stones of Functional Paradigm
1. Immutablity
2. Recursion
3. Functions as first class citizens
4. Higher order functions
5. Lazyness
- High level coding
- Mutability + Shared state = Time
bomb
- Parallelism
- But Threads API is complexe
- For free with streams! Really ?
3.1 Immutablity
static long badIdea() {
// Tables are not thread safe:
// Correctness ?
long[] acc = {0L};
LongStream
.rangeClosed(1, LIMIT)
.parallel()
.forEach( i -> acc[0] += i);
return acc[0];
}
static long lessBadIdea() {
// Thread safe , correct response
// Performance ?
AtomicLong acc = new AtomicLong(0L);
LongStream
.rangeClosed(1, LIMIT)
.parallel()
.forEach( i -> acc.addAndGet(i));
return acc.get();
}
static long sumByReduction() {
return LongStream
.rangeClosed(1, LIMIT)
.parallel()
.sum();
}
3.1 Immutablity
Result: 5000000050000000
in ~288 msResult: 5000000050000000
in ~8306 ms
Result: 1839299186097435
in ~342 ms
static long factorialImperative(final int n) {
long r = 1;
for (int i = 1; i <= n; i++) {
r *= i;
}
return r;
}
static long recFact(final long n) {
return (n == 1) ? 1 : n*recFact(n-1);
}
3.2.Recursion
recFact(5) = 5*recFact(4)
= 5*(4*recFact(3))
= 5*(4*(3*recFact(2)))
= 5*(4*(3*(2*recFact(1)))
= 5*(4*(3*(2*1)))
= 5*(4*(3*2))
= 5*(4*6)
= 5*24
= 120
3.2.Recursion
static long factorialTailRecursive(final long n) {
return factorialHelper(n, 1);
}
private static long factorialHelper(final long n, final long acc) {
return n == 1 ? acc : factorialHelper(n-1, n*acc);
}
3.2.Recursion
FactorialTailRecursive(5) = factorialHelper(5, 1)
= factorialHelper(4, 5)
= factorialHelper(3, 20)
= factorialHelper(2, 60)
= factorialHelper(1, 120)
= 120
3.2.Recursion
Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world");
}
});
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Button Pressed");
}
});
3.3.First class functions
Thread t = new Thread(new Runnable() {
public void run() -> {
System.out.println("Hello world");
}
});
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) -> {
System.out.println("Button Pressed");
}
});
3.3.First class functions
Thread t = new Thread( () -> System.out.println("Hello world") );
button.setOnAction( e -> System.out.println("Button Pressed"));
Lambda expression
(T0 arg0, … , Tn argn) -> Body //T0 … Tn are optional
3.3.First class functions
Method References
ContainingClass::staticMethodName // String::valueOf
containingObject::instanceMethodName //x::toString
ContainingType::methodName //String::toString
ClassName::new //String::new
3.3.First class functions
Is Lambda expression a true function literal ?
3.3.First class functions
java.util.function package to the rescue
• Predicate
• Consumer
• Supplier
• Function
• …
3.3.First class functions
static int sumOfInts(int a, int b) {
return a > b ? 0 : a + sumOfInts(a+1, b);
}
static int sumOfCubes(int a, int b) {
return a > b ? 0 : a*a*a + sumOfCubes(a+1, b);
}
static int sumOfFact(int a, int b) {
return a > b ? 0 : fact(a) + sumOfFact(a+1, b)
}
3.4.Higher order functions
static int higherSum(Function<Integer, Integer> f, int a, int b) {
return a > b ? 0 : f.apply(a) + higherSum(f, a + 1, b);
}
int sumOfSquare = higherSum(x->x*x, 1, 6);
int sumOfCube = higherSum(x->x*x*x, 5, 21);
int sumOfFactoriel = higherSum(x->fact(x), 13, 17);
…..
3.4.Higher order functions
static BiFunction<Integer, Integer, Integer> curriedSum(Function<Integer, Integer> f) {
return
(a, b) -> { return a > b ? 0 : f.apply(a) + curriedSum(f).apply(a+1, b); };
}
// Curryed version call
int sumOfSquare = curriedSum(x->x*x).apply(1, 6);
int sumOfCube = curriedSum(x->x*x*x).apply(5, 21);
int sumOfFactoriel = curriedSum(x->fact(x)).apply(13, 17);
…..
3.4.Higher order functions
List<String>
productsOfInterest(Predicate<Product> pred) {
return Store.products()
.stream()
.filter(pred)
.map(Product::getRef)
.limit(10)
.collect(toList());
}
filterlimit
distinct sotred
map
flatMap
3.5.Lazyness
List<String> productsOfInterest(Predicate<Product>
pred) {
return Store.products()
.stream()
.filter(pred)
.map(Product::getRef)
.limit(10)
.collect(toList());
}
collect
summax
forEachallMatch
anyMatch
oneMatch
count
reduce
3.5.Lazyness
IntStream is = IntStream.iterate(0, n -> n + 1);
int[] r1 = is.filter(n -> n%2 == 0).limit(5).toArray();
System.out.println(Arrays.toString(r1));
int[] r2 = is.filter(n -> n%3 == 0).map(x -> x*x).limit(7).toArray();
System.out.println(Arrays.toString(r2));
3.5.Lazyness
Q&A
References
- http://clojure.org/rationale Mutable stateful objects are the new spaghetti code
- Compositional Programming: http://www.cs.ox.ac.uk/research/pdt/ap/ssgp/slides/odersky.pdf
- http://www.info.ucl.ac.be/~pvr/paradigmsDIAGRAMeng108.pdf
- http://www.ibm.com/developerworks/aix/library/au-aix-multicore-multiprocessor/