The document discusses functional programming concepts including pure functions, immutability, higher-order functions, closures, function composition, currying, and referential transparency. It provides examples of these concepts in JavaScript and compares imperative and declarative approaches. Functional programming in Java-8 is discussed through the use of interfaces to define function types with type inference.
2. What is functional programming?
● Functional programming is a declarative programming paradigm.
● Rooted in Mathematics.
● All computations are the execution of Mathematical functions.
Functional programming is the process of building software by composing pure
functions, avoiding shared state, mutability, and side-effects.
3. Functional Programming Jargons
● Declarative
● Purity
● Immutability
● Higher order
● Closures
● Composition
● Currying
● Referential Transparency
● Execution Order
● ...Recursion, Monads, No side effects, etc etc...
4. Declarative vs
Imperative
Declarative
- Bring me the food please, i am hungry!
Imperative
- Please follow these steps:
1. Take two breads.
2. Put the breads into the toaster.
3. Push down the toaster lever.
4. When toasting is done, take back the breads.
5. Get knife, jam and jelly and a dish.
6. Get some drinking water.
7. Put the meal at my dining table.
Expressions vs Statements!
5. Let’s see an example...
var numbersOneThroughTen = new List<int> { 1, 2, 3, 4, 5, 6, 7,
8, 9, 10 };
//Imperative Approach
var evenNumbers = new List<int>();
foreach (var number in numbersOneThroughTen) {
if (number % 2 == 0) {
evenNumbers.Add(number);
}
}
//Declarative Approach
var evenNumbers = numbersOneThroughTen.Select(n => n % 2 == 0);
7. Pure Functions
A pure function is just like a mathematical function
where the return value is only determined by its
input values, without any observable side effects.
8. f(x) = x+2; => A pure function.
➔ Provides the output which always will be greater than 2 as its input.
➔ Doesn’t have any access to the outside of its context, so doesn’t have any access
to application states (data), and hence no side effects.
➔ Never makes any change on its input value, just produces a output by using
expressions.
➔ Contextually deterministic.
Math.random() == 65; Math.random() == 763; Math.random() == 8 =>Impure
➔ Output is not consistent.
➔ Uses external sources/states to perform some operation.
➔ Contextually non - deterministic.
Example of pure functions
9. Traits of a pure function
● Pure functions must take at least one input.
● Pure functions must return something.
● Pure functions will always return the same output given the same input.
● Pure functions have no side effects.
10. public class SideEffect {
private int x;
private void affect(int y){
x = x+y;
System.out.println(x);
}
public static void main(String[] args) {
SideEffect sideEffect = new SideEffect();
sideEffect.affect(5); //5
sideEffect.affect(5); //10
sideEffect.affect(5); //15
}
}
An impure Function with side effect
11. How do i do anything with only pure functions?
writeFile(fileName);
updateDatabaseTable(sqlCmd);
sendAjaxRequest(ajaxRequest); => Some impure functions
openSocket(ipAddress);
➔ In Functional Programming, you don’t just write Pure Functions.
➔ Functional Languages can’t eliminate Side Effects, they can only confine
them.
➔ Since programs have to interface to the real world, some parts of every
program must be impure.
12. Immutability
Immutability is not about forbidding change at all. Instead
immutability is more on how to handle change.
Immutability is not about forbidding some kind of
operations. You still can add an element to an array,
the difference is that you just do it differently.
13. Mutability is illegal in functional programming
Do you remember when you saw the first following bit of code?
var x = 1;
x = x + 1;
That scary moment! In math, x can never be equal to x + 1.
But in Imperative Programming, it means, actually we all know what it is…
Well, in functional programming, x = x + 1 is illegal. So you have to remember
what you forgot in math… Sort of.
There are no variables, no loops, no statements in Functional Programming. There are constants,
expressions and ofcourse functions.
14. How immutability helps us?
➔ No more waiting, no more race conditions, no more concurrency controlling
overhead - for a multithreaded environment.
➔ No accidental mutations.
➔ Thread safe data and algorithms.
15. Higher order
Functions
A higher order function is a function which takes a
function as it’s input and produces either
function or data or both as it’s output.
In functional programming, all functions are higher
order functions.
16. Some mathematical examples
F(x) = x2
+3x+1
G(x) = 2x
➔ We put x = 2 at F(x), we get F(2) = 11; A co-domain.
➔ When we put x = G(x), we get, F(G(x)) = F(2x) = 4x2
+6x+1; Another function.
In functional programming, functions are just the mathematical functions,
and all functions are higher ordered.
17. Why higher order functions?
Let’s think about refactoring for a minute. Here’s some Javascript code:
function validateSsn(ssn) {
if (/^d{3}-d{2}-d{4}$/.exec(ssn)) console.log('Valid SSN');
else console.log('Invalid SSN');
}
function validatePhone(phone) {
if (/^(d{3})d{3}-d{4}$/.exec(phone)) console.log('Valid Phone Number');
else console.log('Invalid Phone Number');
}
Here is the refactored code:
function validateValue(value, regex, type) {
if (regex.exec(value)) console.log('Invalid ' + type);
else console.log('Valid ' + type);
}
18. function validateAddress(address) {
if (parseAddress(address)) console.log('Valid Address');
else console.log('Invalid Address');
}
function validateName(name) {
if (parseFullName(name)) console.log('Valid Name');
else console.log('Invalid Name');
}
How do we refactor this?
At this moment, we have already started feeling the necessity of higher orders…
function validateValueWithFunc(value, parseFunc, type) {
if (parseFunc(value)) console.log('Invalid ' + type);
else console.log('Valid ' + type);
}
But what happens when you face the following situation?
19. Traits of a higher order function
➔ Takes one or more functions as arguments
➔ Returns either a function or data or both as its result.
➔ When passing a function to another function, data’s of within a lexical scope are
captured. This property is called Closure!
20. Closure
“Closure is when a function is able to remember and
access its lexical scope even when that function is
executing outside its lexical scope.”
- Kyle Simpson
21. A contrived example of closure!
function grandParent(g1, g2) {
var g3 = 3;
return function parent(p1, p2) {
var p3 = 33;
return function child(c1, c2) {
var c3 = 333;
return g1 + g2 + g3 + p1 + p2 + p3 + c1 + c2 + c3;
};
};
}
Here is how it works...
var parentFunc = grandParent(1, 2); // returns parent
var childFunc = parentFunc(11, 22); // returns child
console.log(childFunc(111, 222)); // prints
738 //1+2+3+11+22+33+111+222+333=738
22. Traits of the closure!
➔ A closure is a function’s scope that’s kept alive by a reference to that function.
➔ Closure captures the image of all data within a lexical scope of a function, this
captured image is alive until the function is referenced to a new container.
➔ Closure can be implemented in a different way depending on if the scope is
lexical or dynamic, still purposes the same result.
23. Function
Composition
In Functional Programming, functions are our building
blocks. We write them to do very specific tasks and
then we put them together like Lego™ blocks.
This is called Function Composition.
24. Let’s start with two javaScript functions...
var add10 = function(value) {
return value + 10;
};
var mult5 = function(value) {
return value * 5;
};
This is too verbose so let’s rewrite it using fat arrow notation:
var add10 = value => value + 10;
var mult5 = value => value * 5;
25. Now let’s imagine that we also want to have a function that takes a value and adds 10
to it and then multiplies the result by 5.
We could write:
var mult5AfterAdd10 = value => 5 * (value + 10)
Do we really want to do this? No!
So instead, let’s use add10 and mult5 to build our new function:
var mult5AfterAdd10 = value => mult5(add10(value));
Composition
26. How do we compose functions in Math?
In math, f g∘ is functional composition and is read “f composed with g”or, more
commonly, “f after g”. So (f g)(x)∘ is equivalent to calling f after calling g with x or
simply, f(g(x)).
And that’s exactly what we did. We called mult5 after we called add10 with value or
simply, mult5(add10(value)).
27. Since Javascript doesn’t do Function Composition natively, let’s look at Elm*:
add10 value =
value + 10
mult5 value =
value * 5
mult5AfterAdd10 value =
(mult5 << add10) value
The << infixed operator is how you compose functions in Elm. It gives us a visual sense of how the
data is flowing
You can compose as many functions as you like this way:
f x = (g << h << s << r << t) x
*ELM is a JavaScript Based pure functional language.
Composition in a pure functional language
28. Currying Functions
In mathematics and computer science, currying is the
technique of translating the evaluation of a function
that takes multiple arguments into evaluating a
sequence of functions, each with a single argument.
- Wikipedia
29. A different version of our ‘add’ function(s)
//Non Curried Version
function add (a, b) {
return a + b;
}
add(3, 4); // returns
7
//Curried Version
function add (a) {
return function (b)
{
return a + b;
}
}
add(3)(4); // returns
7
var add3 = add(3);
add3(4); // returns 7
30. Why currying Functions?
➔ More purity
➔ More Testability
➔ Partial operations
◆ F(x)(y)(z)......(p)
◆ x , y, z ….p; So many operations and each are long computations
◆ Modularity and Single Responsibility
31. At this point we will delve into some
common functional Functions.
32. What is mapping actually?
f(x) = (x+1)2
1
2
-1
-2
4
9
0
1
Domain Co-Domain
33. Let’s be more practical!
We want to map every data of a list by a “Specified” mapping criteria.
var map = (apply, array) => {
var newArray = [];
for (var i = 0; i < array.length; ++i) {
newArray[i] = apply(array[i]);
}
return newArray;
};
var things = [1, 2, 3, 4];
var newThings = map(v => v * 10, things);
If you can recall your use of map in Java-8 stream API, well we have just reached
there, this is that map!
34. Filter
We want to filter some data of a list by a “Specified” Criteria.
var filter = (predicate, array) => {
var newArray = [];
for (var i = 0; i < array.length; ++i) {
if (predicate(array[i]))
newArray[newArray.length] = array[i];
}
return newArray;
};
var numbers = [1, 2, 3, 4, 5];
var oddNumbers = filter(x => x % 2 !== 0, numbers);
console.log(oddNumbers); // [1, 3, 5]
Recall your use of filter function in Java-8 stream API, exactly the same thing!
35. Reduce/Fold
We want to reduce/fold a list of data by a “Specified” Criteria.
One example of using a reduce/fold function would be finding the central
tendency of a list of data.
var reduce = (f, start, array) => {
var acc = start;
for (var i = 0; i < array.length; ++i)
acc = f(array[i], acc); // f() takes 2 parameters
return acc;
});
var values = [1, 2, 3, 4, 5];
var sumOfValues = reduce((x, y) => x + y, 0, values);
console.log(sumOfValues); // 15
36. Referential
Transparency
When two actions, without any side effects, have the
same results/outputs, then these two actions are
interchangeable in terms of satisfying a need.
37. Some real life example
When i am coming back to Dhaka from my home town after the vacation, on the way
i meet with you and you ask me, “Where are you going?”, I reply, “I’m going to
Dhaka”.
Notice, my reply can be replaced with all the below expressions.
➔ I’m going to the capital city of Bangladesh.
➔ I’m going to the 4th most polluted city of this world!
➔ I’m going to the largest city of Bangladesh.
➔ I’m going to the 4th most densely populated city!
Referentially above all expressions are valid and transparent!
38. Referential Transparency in Functional
Programming
F(x,y) and G(x,y) are two pure functions without any side effects.
At some point we do know that, F(a,b) and G(c,d) gives us the same value.
One function is referentially transparent to another.
var x = F(a,b) = G(c,d) // Given that two functions don’t have
any side effects.
F(x) + F(x) = 2(F(x))
It seems so absurd in imperative world but in functional programming, it is true!
39. Order of Execution
The order of execution in a Pure Functional Language
can be determined by the compiler.
40. Let’s rewind back where we started...
Imperative steps to prepare the breakfast meal for you:
1. Take two breads.
2. Put the breads into the toaster.
3. Push down the toaster lever.
4. When toasting is done, take back the breads.
5. Get knife, jam and jelly and a dish.
6. Get some drinking water.
7. Put the meal at my dining table.
41. Putting into threads
Thread 1:
1. Take two breads.
2. Put the breads into the toaster.
3. Push down the toaster lever.
4. When toasting is done, take back the breads.
Thread 2:
5. Get knife, jam and jelly and a dish.
6. Get some drinking water.
7. [ …..Waiting is the most painful task in this world! ]
8. Put the meal at my dining table.
42. What if the order was not a matter?
➔ We can ignore the order only when we make our functions pure.
➔ As the pure function does not have any side effect, order does not
matter in this case.
➔ In a pure functional programming, compiler can recognize the pure
functions, and thus compiler can also determine the order of execution
of those functions.
➔ With the purity and no side effects of our functions, we can take the highest
degree of advantages from the CPU.
44. How do we make our “actions/behaviors”
portable in Java-7 or earlier versions?
➔ How do we pass any kind of task/action to Threads? Thread class does not care
about which task i provide, Thread just runs whatever task we pass to it.
➔ How do we compare two objects? When we have so many different combinations
of fields to take as parameters of comparison logic?
➔ Think about the Call-Back pattern, how do we implement that?
45. Making the functions portable in Java-8
Again let’s think about porting a function,
➔ Create a function on the fly
➔ Pass it to some variable or container
Java is a strictly typed language, we just can’t pass anything (function) to any
variable or container, and we don’t have so called “VAR”.
There is a funny proverb: “Everything is fair, in Love and VAR”
We don’t have VAR yet still in Java-8 :( [Wait for Java-10 to be released]
But we have Interface :)
46. Interface as the functional type with Type Inference
public class Sample {
public static void main(String[] args) {
Calculator c = (x,y) -> x+y;
c.calc(2,3); //5
Calculator c2 = Sample::calc; //Method Reference
c2.calc(2,3); //5
}
private static int calc(int x, int y) {
return x+y;
}
@FunctionalInterface
interface Calculator{
int calc(int x, int y);
}
}
47. Interface as the functional type
Why Interface?
Why not a new Functional type?
-Backward Compatibility
48. Some Common Built-in Functional Interfaces
● Function<T,R>
○ Represents a function that accepts one argument and produces a result.
● BiFunction<T,U,R>
○ Represents a function that accepts two arguments and produces a result.
● Consumer<T>
○ Represents an operation that accepts a single input argument and returns no result.
● BiConsumer<T,U>
○ Represents an operation that accepts two input arguments and returns no result.
● Supplier<T>
○ Represents a supplier of results.
● Predicate<T>
○ Represents a predicate (boolean-valued function) of one argument.
● BiPredicate<T,U>
○ Represents a predicate (boolean-valued function) of two arguments.
Full list is here:
https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
49. Higher Order Function with Type Inference in Java-8
public static void main(String[] args) {
//F(x) = x^2 + 5x + 6
//G(x) = 2x
//H(x) = FoG(x) = F(G(x)) = 4x^2 + 10x + 6
//H(5) = 156
Function<Function<Integer, Integer>, Function<Integer, Integer>> Fx =
f1 -> (f2 -> f1.apply(f2)*f1.apply(f2) + 5*f1.apply(f2) +6);
Function<Integer, Integer> Gx = x -> 2*x;
Function<Integer, Integer> Hx = Fx.apply(Gx); //FoG
System.out.println(Hx.apply(5)); //prints 156
}
50. Function Composition in Java-8
//F(x) = √(x^2 + 5x + 6)
//F(2) = ?
public static void main(String[] args) {
Function<Integer, Integer> squareAndMult5 = x -> x*x + 5*x;
Function<Integer, Integer> add6 = x -> x+6;
Function<Integer, Double> sqrt= Math::sqrt; //x -> Math.sqrt(x);
Double result = squareAndMult5.andThen(add6).andThen(sqrt).apply(2);
System.out.println(result); //4.47213595499958
}
If it was a pure functional language then composition would be like this:
(sqrt << add6 << squareAndMult5)2
51. Function Currying in Java-8
//F(x,y,z) = x^2 + y^2 + z^2
//F(2,y,z) = 4 + y^2 + z^2
//F(2,3,z) = 13+z
//F(2,3,4) = 29
public static void main(String[] args) {
Function<Integer, Function<Integer, Function<Integer, Integer>>> Fxyz
= x -> y -> z -> x*x + y*y + z*z;
Function<Integer, Function<Integer, Integer>> Fyz = Fxyz.apply(2);
Function<Integer, Integer> Fz = Fyz.apply(3);
Integer result = Fz.apply(4);
System.out.println(result); //29
Fxyz.apply(2).apply(3).apply(4);
System.out.println(result); //29
}
52. Closure and Effective Final in Java-8
public static void main(String[] args) {
int a = 2;
Processor processor = (x, y) -> x + y + a;
Integer result = processor.process(2,3);
System.out.println(result);
}
@FunctionalInterface
interface Processor {
int process(int x, int y);
}
➔ Value of “a” is captured when the “process” function is referenced.
➔ Variable “a” and function “process” are in different lexical scope.
➔ Value of “a” is passed with function “process”.
➔ Value of “a” is supposed to be constant within the life-cycle of “process” function.
53. Closure and Effective Final in Java-8
public static void main(String[] args) {
int a = 2;
Processor processor = (x, y) -> {
a = 3; //Compile time error: Variable used in Lambda
//expression should be effectively final.
return x + y + a;
};
Integer result = processor.process(2,3);
System.out.println(result);
}
@FunctionalInterface
interface Processor {
int process(int x, int y);
}
54. Streams
A sequence of Elements, supporting sequential and
parallel aggregate operations.
55. Streams Overview
Collection Streams Operations
Terminal
?
Close Streams
No
Yes
A Stream pipeline consists of three types of things:
1. Collection of Elements
2. Zero or more Intermediary Operations
3. A Terminal Operation
a. Which normally gives a result or Side effect.
56. Streams Example
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
int sum = integers.stream() //Produce Stream pipeline
.filter(x -> x % 2 == 0) //Filter even numbers; another stream
.mapToInt(x -> x * 2) //Map each to it’s double; another stream
.sum(); //Sum; terminal operation; Stream closed
System.out.println(sum); //Prints 60
List<String> geniusPeople =
Arrays.asList("J.Bloch", "R.Fielding", "B.Goetz", "BrendanEich");
geniusPeople.parallelStream() //Elements are divided into chunks
.filter(name -> name.startsWith("B"))
.findFirst()
.ifPresent(System.out::println); //BrianGoetz
57. Lazy Evaluation of Streams
➔ Streams are lazy, intermediate operations are not evaluated until terminal operation
is invoked.
➔ Each intermediate operation creates a new stream, stores the provided
operation/function and return the new stream.
➔ The pipeline accumulates these newly created streams.
➔ When terminal invokes, traversal of streams begins, associated functions are
performed one by one.
➔ Parallel streams don't evaluate streams 'one by one'. The operations are rather
performed simultaneously, depending on the available cores.
58. Example of Lazy Evaluation of Streams
IntStream stream = IntStream.range(1, 5)
.filter(i -> {
log("filtering", i);
return i % 2 == 0;
});
log("Invoking terminal method"); //This line logs first
log("The sum is", stream.sum()); //Backtracking starts from this line
Output:
23:34:06.828 - Invoking terminal method
23:34:06.834 - filtering - 1
23:34:06.835 - filtering - 2
23:34:06.837 - filtering - 3
23:34:06.838 - filtering - 4
23:34:06.839 - The sum is - 6
*log() is a user defined method
to console the time of events.