Functional Programming with Java 8 by Venkat Subramaniam

Function Programming’s Promise

We have

i) Inherent complexity: complexity stemming from domain

ii) Accidental complexity: complexity we create when trying to deal with the inherent complexity

Functional programming removes much of the accidental complexity

Mutable State

Code is becoming more concurrent

Issues with mutable state:

* Sharing mutable state is error prone
* Mutable states are hard to reason about
* Hard to make sequential code into concurrent code

Functional Programming Properties

Functional is a layer of abstraction above imperative programming.

Assignment-less Programming

Much like we don’t use gotos in our code, but it’s ok for the compiler to use gotos (or equivalent) under-the-hood

goto is to imperative/structured programming

what

assignment is to functional programming

Immutable State

We can make smart copies of object.

Let the under-the-hood mutate the object, while we view and think of the object as immutable.

Functions as First-Class Citizens

We can treat functions like we do objects

A higher-order function can (one or more):

* be passed functions as paramters
* create functions itself
* return functions

Pure Functions

Pure functions don’t have side-effects

i) does not change some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value, such as mutation of mutable objects (we want deterministic output to our input and the output is solely under the form of the returned value)

ii) does not depend on anything that changes as well: The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program * We want lazy evaluation

Functional Style vs (Purely) Functional Programming

Language provides higher-order functions: functional style (Java, C#)

Language prevents mutability: functional programming (Haskell)

Lambda Expression (Anonymous Function)

Only parameters list, body.

Inferred return, no name needed.

Passed to higher-order functions

Familiar vs Simple

Complex (not simple) things seem easy to think about when they become familiar. Familiarity with something does not imply that thing is simple.

External to Internal Iterators

Given

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

External Iterators

for (int i = 0; i < numbers.size();  i++){
         System.out.println(numbers.get(i));
}

OR

for(int e: numbers){
   System.out.println(e);
}

Internal Iterators Iteration on auto-pilot, no more boiler-plate

Polymorphic because forEach() can change but you’re not affected

numbers.forEach((Integer E) -> System.out.println(e));

Or even type inference in Java

numbers.forEach((e) -> System.out.println(e));

Or even method reference

numbers.forEach(System.out::println);

Lazy Evaluation and Immutability

Given

List<Integer> numbers = Arrays.asList(1,2,3);

We have a lambda that relies on the external array factor

int[] factor = new int[] { 2 };
Steam<Integer> strm = numbers.stream().map(e -> e * factor[0]);

factor[0] = 0 
strm.forEach(System.out::println)

This makes the lambda not pure; it relies on external elements that can change.

While we initially expected the printed output to be 2, 4, 6 we’ll be getting 0,0,0 This is because the lazy evaluation happens after the change.

Function Composition (Pipelining)

System.out.println(
   numbers.stream().
   .filter(e -> e % 2 == 0) // this is 
   .mapToDouble(Math::sqrt) // function composition (pipelining)
   .sum() //only evaluated here, not in any previous function of the pipeline
);

Functional Benefits

Code Clarity: Easier to understand the intent

Fewer Errors: No accidental complexity (thoughts: or just less) Less mutability, as demanded by the functional style, also leads to less hard to see, hard to catch mutability errors

Easier Parallelization: In imperative, sequential code looks very different from concurrent. In functional, code structure is the same, with small simple and evident changes e.g. one extra function in the pipeline

numbers.stream().
  .parallel()
  .filter(e -> e % 2 == 0) // this is
  .mapToDouble(Math::sqrt) // function composition (pipelining)
  .sum() //only evaluated here, not in any previous function of the pipeline

[Thoughts: Boilerplate is not needed, slips and mistakes in boilerplate is reduced

When certain imperative constructs become complex, there might be edge cases that aren’t obvious

Without the need for continually coding very-similar-but-not-always identical boilerplate code, these non-obvious mistakes are prevented]