-
Notifications
You must be signed in to change notification settings - Fork 48
Functional Programming
- 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
- Always return a value (a void function is therefore not pure)
- Do not mutate data (a change in state is represented by new data)
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]
Peer Learning
Codecrunch Contributions
Piazza Contributions
Wiki Contributions
Guides
Setting Up Checkstyle
Setting Up Java
Setting Up MacVim
Setting Up Sunfire
Setting Up Unix For Mac
Setting Up Unix For Windows
Setting Up Vim
Setting up SSH Config
CS2030 Contents
Lecture 1 SummaryCompile-run vs Run-time Summary
Quick Guide To Abstraction
Generics and Variance of Types
Comparable vs Comparator
Summary of completable future
CS2030S Notes
ELI5 Optional.map vs Optional.flatMap
PECS Example Code
Java Collection Framework (Iterator)
Generic
Generic Type Parameter and Generic Wildcard
Calculator
Lambda-Expression
Single Abstract Method (SAM)
Method Reference
Functional Interfaces 2
Simple Usage of Sandbox
Associative-but-not-commutative
Higher Order function
Functional Programming
Calculator With Functor
Eager Evaluation VS Lazy Evaluation
Simple Usage of Lazy Evaluation
Lazy Evaluation for LazyList
Lazy Evaluation for BinaryTree
Stream
Parallel Stream
Optional
Simple Usage of Stream
Asynchronous Programming
Notes on CompletableFuture
Notes on CompletableFuture 2
Simple Usage of CompletableFuture
Mind Map
Exception Handling
Links
CS2030 Java Style Guide
CS2030 Javadoc Specification
JDK 11 Download Link
JDK 11 API Docs
Codecrunch
Piazza Forum