D. Turner, Addison-Wesley, 1990
Read this post in spanish
Modularity and readability are two of the most important features of code in order to achieve high quality and maintainable code, but writing modular code create the need for "glue code" that some times include side effects and reduce the readability. Functional programming use high-order functions and lazy evaluation in order to improve or even avoid this "glue code" and guarantee the readability with the referential transparency.
Referential Transparency
Function of FP share their definition with the math concept as follow : "A relation from a set of inputs to a set of possible outputs where each input is related to exactly one output" , consequently there is no side effects!. Additionally this allow us to replace a function call with the return value without having to view the whole context of the function and create more reusable and easy to test "modules".
High Order Functions
If a function return another function or have functions as input it's considered a high order function, it's also called first-class citizen in a more software-related approach.
Using high order functions we can define some abstract behaviors that apply to multiple data types and requires less "glue code".
The foldLeft function of collections in VAVR is a simple example of how this work:
The examples use a JAVA library called VAVR
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer r = list.foldLeft(0, Integer::sum); // 45
}
//source code of VAVR
default <U> U foldLeft(U zero, BiFunction<? super U, ? super T, ? extends U> f) {
Objects.requireNonNull(f, "f is null");
U xs = zero;
for (T x : this) {
xs = f.apply(xs, x);
}
return xs;
}
Lazy Evaluation
Since we don't have side effects in FP it's possible write the definition of a value without evaluate them until the runtime execution and don't overthink about the infinite possible escenarios created by side effects.
A great example of this feature is the square root function that use a "infinite" list of candidates and choose the first value to pass the precision required.
public static Function1<Double, Double> nextCandidate(Double n){
/**
* Sequence of candidates for the square root according to Newton-Raphson
*/
return Function1.of((previous) -> (previous + (n / previous)) / 2d);
}
public static Double squareRoot(Double n){
Stream<Double> s = Stream.iterate(1d, nextCandidate(n));
Double epsilon = 0.0001d; // precision required
return s.takeUntil(candidate -> Math.abs((candidate * candidate) - n) < epsilon).last();
}
public static void main(String[] args) {
System.out.println(squareRoot(25d)); // 5.000023178253949
}
The previous example also show another example of high order functions, represented by nextCandidate which have a function as output.
Feel free to comment about your experience with Functional programming in the comments.
This is my first English post and the firs of a serie of papers summaries, so I appreciate the feedback about the writing style, the content, other papers to read and recommended tags for the serie :)
Top comments (3)
Interesting you approach FP with Java examples, functional language examples would have come handy too (can be cleaner of Java lib calls).
I know a bit of Scala and sure the code it's cleaner and there is a lot of awesome resource that JAVA doesn't have, but I learn functional in a project that 'require JAVA' because the client force that decision and there is so many projects that have to use this language for some reasons so we find VAVR and I think is good to show this can of alternatives.
I see your case, and it makes sense. VAVR creators specifically say they offer data structurures, not all for functional purity (eg IO monads): github.com/vavr-io/vavr/issues/20#.... If I was on Java path, I'd say I use some of elements of fucntional programming, but not the full stack.