Functional Programming - HolmesJJ/OOP-FP GitHub Wiki
- Simplify the codes and improve coding efficiency
- Reduce performance wastes
The same input will always get the same output without any significant side effects
The interactions with the external environment of the function are all side effects
-
Cannot perform I/O (
System.out.println();
) - Cannot throw exceptions
-
Cannot use
Random()
(same input will get different output) - Cannot send http request
- Change the variables outside the function cannot affect the function
A functor is a Generic Type that contains a thing (also known as payload), and provides a Constructor (make()
, also known as of()
), and a map()
method that transforms the payload.
Law | Description | Explanation |
---|---|---|
Identity | The value of make(t).map(x -> x) equals to the value of make(t) , which is make(t).map(x -> x).get() equals to make(t).get()
|
make(t) : t make(t).map(x -> x) : t |
Associativity | The value of make(t).map(f).map(g) equals to the value of make(t).map(f.andThen.g) , which is make(t).map(f).map(g).get() equals to make(t).map(f.andThen.g).get()
|
make(t) : t make(t).map(f) : f(t) make(t).map(f).map(g) : g(f(t)) make(t) : t f.andThen.g: g(f(x)) make(t).map(f.andThen.g) : g(f(t)) |
Class | Constructor | Method |
---|---|---|
Optional<T> |
of() |
map() |
Stream<T> |
of() |
map() |
ArrayList<T> |
Arrays.asList() |
None (Can be provided as following example below) |
Function<T, U> |
Java’s assignment of Lambda expression | andThen() |
CompletableFuture<T> |
runAsync() , supplyAsync() , failedFuture() , completedFuture() , etc. |
thenApply() |
<T,U> ArrayList<U> map(Function<T,U> f, ArrayList<T> list) {
ArrayList<U> result = new ArrayList<>();
for (T item: list) {
result.add(f.apply(item));
}
return result;
}
<T> ArrayList<T> filter(Predicate<T> p, List<T> list) {
ArrayList<T> result = new ArrayList<>();
for (T item : list) {
if (p.test(item)) {
result.add(item);
}
}
return result;
}
// The Constructor is Java’s assignment of Lambda expression
Function<Boolean, String> boolStringFunc = v -> v ? "hello" : "goodbye";
// the map() method is andThen()
boolStringFunc.andThen(String::length);
- Functor is a container.
- The general convention of Functional Programming is that the functor has a Constructor
of()
to generate a new container. - Functor contains the value, which is passed in by the Constructor
of()
. - Functor has a
map()
method, which maps each value in the container to another container. - The calculations in Functional Programming are all done by Functor and do not directly operate the value in the Functor.
- The functor itself has an external interface (
map()
method). The function passed in via the interface (map()
method) is the operator to change the value in the Functor.
A Monad is a Parametrized Type that contains a thing, along with a Constructor (of()
, also known as unit()
), and a method flatmap()
(also known as bind()
).
Law | Description | Explanation |
---|---|---|
Left Identity |
of(t).flatMap(f) == f(t), which is of(t).flatMap(f).get() equals to f.apply(of(t).get()).get()
|
of(t) : t of(t).flatMap(f) : f(t) |
Right Identity | The value of of(t).flatMap(t -> of(t)) equals to the value of of(t) , which is of(t).flatMap(t -> of(t)).get() equals to of(t).get()
|
of(t) : t of(t).flatMap(x -> of(x)) : t (f(t) = t) |
Associativity | The value of of(t).flatMap(f).flatMap(g) equals to the value of of(t).flatMap(t -> f(t).flatMap(g)) , which is of(t).flatMap(f).flatMap(g).get() equals to of(t).flatMap(t -> f(t).flatMap(g)).get()
|
of(t) : t of(t).flatMap(f) : f(t) of(t).flatMap(f).flatMap(g) : g(f(t)) of(t) : t t -> f(t).flatMap(g)) : g(f(t)) of(t).flatMap(t -> f(t).flatMap(g)) : g(f(t)) |
/**
* t: [Arrays.asList("A", "B"), Arrays.asList("C", "D"), Arrays.asList("E", "F")]
* f: x -> of(x) ===> List<String> -> Stream<List<String>>
* of(t).flatMap(x -> of(x)) == of(t)
*/
public static void rightIdentityExample() {
Stream<List<String>> stream = Stream.of(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "F")
);
System.out.println(stream.collect(Collectors.toList()));
Stream<List<String>> stream2 = Stream.of(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "F")
);
Stream<List<String>> stream3 = stream2
.flatMap(new Function<List<String>, Stream<List<String>>>() {
@Override
public Stream<List<String>> apply(List<String> strings) {
return Stream.of(strings);
}
});
System.out.println(stream3.collect(Collectors.toList()));
}
/* Output */
[[A, B], [C, D], [E, F]]
[[A, B], [C, D], [E, F]]
public static void associativityExample() {
/**
* of(t).flatMap(f).flatMap(g)
*/
Stream<List<String>> stream = Stream.of(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "F")
);
Stream<String> stream2 = stream
.flatMap(new Function<List<String>, Stream<String>>() {
@Override
public Stream<String> apply(List<String> strings) {
return strings.stream();
}
}).flatMap(new Function<String, Stream<String>>() {
@Override
public Stream<String> apply(String strings) {
return Stream.of(strings);
}
});
System.out.println(stream2.collect(Collectors.toList()));
/**
* of(t).flatMap(x -> f(x).flatMap(g))
*/
Stream<List<String>> stream3 = Stream.of(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "F")
);
Stream<String> stream4 = stream3
.flatMap(new Function<List<String>, Stream<String>>() {
@Override
public Stream<String> apply(List<String> strings) {
Stream<String> s = strings.stream();
return s.flatMap(new Function<String, Stream<String>>() {
@Override
public Stream<String> apply(String strings) {
return Stream.of(strings);
}
});
}
});
System.out.println(stream4.collect(Collectors.toList()));
}
/* Output */
[A, B, C, D, E, F]
[A, B, C, D, E, F]
Class | Constructor | Method |
---|---|---|
Optional<T> |
of() |
flatMap() |
Stream<T> |
of() |
flatMap() |
ArrayList<T> |
Arrays.asList() |
None (Can be provided as following example below) |
CompletableFuture<T> |
runAsync() , supplyAsync() , failedFuture() , completedFuture() , etc. |
thenCompose() |
<T,U> ArrayList<U> flatmap(Function<T, List<U>> f, List<T> list) {
ArrayList<U> result = new ArrayList<>();
for (T items : list) {
result.addAll(f.apply(items));
}
return result;
}
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.function.Function;
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
System.out.println(
map(String::toUpperCase,
intoWords("The rain in Spain falls mainly in the plain"))
);
System.out.println(
map(String::toUpperCase,
filter(s -> isEven(s.length()),
intoWords("The rain in Spain falls mainly in the plain")))
);
System.out.println(
map(Main::stringReverse,
filter(word -> word.contains("i"),
intoWords("The rain in Spain falls mainly in the plain")))
);
System.out.println(
map(Main::stringToList,
intoWords("CS2030 is great fun"))
);
System.out.println(
flatmap(Main::stringToList,
intoWords("CS2030 is great fun"))
);
}
public static <T,U> ArrayList<U> map(Function<T,U> f, ArrayList<T> list) {
ArrayList<U> result = new ArrayList<>();
for (T item: list) {
result.add(f.apply(item));
}
return result;
}
public static <T,U> ArrayList<U> flatmap(Function<T, List<U>> f, List<T> list) {
ArrayList<U> result = new ArrayList<>();
for (T items : list) {
result.addAll(f.apply(items));
}
return result;
}
public static <T> ArrayList<T> filter(Predicate<T> p, List<T> list) {
ArrayList<T> result = new ArrayList<>();
for (T item : list) {
if (p.test(item)) {
result.add(item);
}
}
return result;
}
public static ArrayList<String> intoWords(String sentence) {
ArrayList<String> result = new ArrayList<>();
//Add each word into the result list
new Scanner(sentence).forEachRemaining(result::add);
return result;
}
public static boolean isEven(int n) {
return n % 2 == 0;
}
public static String stringReverse(String s) {
return new String(new StringBuffer(s).reverse());
}
public static ArrayList<String> stringToList(String s) {
ArrayList<String> result = new ArrayList<>();
for (char c : s.toCharArray()) {
result.add(new String(new char[]{c}));
}
return result;
}
}
/* Output */
[THE, RAIN, IN, SPAIN, FALLS, MAINLY, IN, THE, PLAIN]
[RAIN, IN, MAINLY, IN]
[niar, ni, niapS, ylniam, ni, nialp]
[[C, S, 2, 0, 3, 0], [i, s], [g, r, e, a, t], [f, u, n]]
[C, S, 2, 0, 3, 0, i, s, g, r, e, a, t, f, u, n]