Functional programming (FP) is a programming style that focuses on functions and minimizes changes of state (using immutable data structures). It is closer to expressing solutions mathematically, rather than through step-by-step instructions.
In FP, functions should be “side-effect free” (nothing outside the function is changed) and referentially transparent (a function returns the same value every time when given the same arguments). For example, this would allow values to be cached (saved in memory).
FP is an alternative to the more common imperative programming, which is closer to telling the computer the steps to follow.
Although functional programming could be achieved in Java pre-Java-8,1 Java 8 enabled language-level FP support with lambda expressions and functional interfaces.
Java 8, JavaScript, Groovy, and Scala all support functional-style programming, although they are not FP languages.
Prominent functional programming languages such as Common Lisp, Scheme, Clojure, Racket, Erlang, OCaml, Haskell, and F# have been used in industrial and commercial applications by a wide variety of organizations. Clojure2 is a Lisp-like language that runs on the JVM.
Functions and Closures
Functions as a first-class feature is the basis of functional programming. First-class feature means that a function can be used anywhere a value can be used.
Here, the functional interface is Function<T,R>, which has the apply method—T being the parameter type and R being the return type. The return value and parameter type are both Integers, thus Integer,Integer are the generic type parameters.
In Java 8, a functional interface is defined as an interface with exactly one abstract method. This even applies to interfaces that were created with previous versions of Java.
Although both Java and Scala are statically typed, Scala actually uses the right-hand side to infer the type of function being declared, whereas Java does the opposite in most cases. Java 11 introduced the local variable type var which allows syntax very close to Scala’s var.
In Java, Groovy, and Scala, the return keyword can be omitted, if there is one expression in the function/closure. However, in Groovy and Scala, the return keyword can also be omitted, if the returned value is the last expression.
Map, Filter, etc.
Once you have mastered functions, you quickly realize that you need a way to perform operations on collections (or sequences or streams) of data. Because these are common operations, sequence operations, such as map, filter, reduce, etc., were invented.
Immutability
Immutability and FP go together like peanut butter and jelly. Although it’s not necessary, they blend nicely.
In purely functional languages, the idea is that each function has no effect outside itself—no side effects. This means that every time you call a function, it returns the same value given the same inputs.
To accommodate this behavior, there are immutable data structures. An immutable data structure cannot be directly changed but returns a new data structure with every operation.
So, in the preceding, map would remain unchanged.
Scala uses the val keyword to denote immutable values, as opposed to var, which is used for mutable variables.
Java has the final keyword for declaring variables immutable (this only stops the value from being modified, if it’s a reference to another object, that object’s variables could still be modified).
In addition to the final keyword , Groovy includes the @Immutable annotation3 for declaring a whole class immutable.
JavaScript uses the const keyword.4
Java
In Java 8, the Stream<T> interface was introduced. A stream is like an improved iterator that supports chaining methods to perform complex operations.
Collection's stream() method or parallelStream() method: These create a stream backed by the collection. Using the parallelStream() method causes the stream operations to be run in parallel.
Arrays.stream() method: Used for converting arrays to streams.
Stream.generate(Supplier<T> s): Returns an infinite sequential stream in which each element is generated by the given supplier.
Stream.iterate(T seed, UnaryOperator<T> f): Returns an infinite sequential ordered stream produced by iterative application of a function to an initial element seed, producing a stream consisting of seed, f(seed), f(f(seed)), etc.
Groovy
findAll: Much like filter, it finds all elements that match a closure.
collect: Much like map, this is an iterator that builds a collection.
inject: Much like reduce, it loops through the values and returns a single value.
each: Iterates through the values using the given closure.
eachWithIndex: Iterates through with two parameters: a value and an index (the index of the value, starting at zero and going up).
find: Finds the first element that matches a closure.
findIndexOf: Finds the first element that matches a closure and returns its index.
any: True if any element returns true for the closure.
every: True if all elements return true for the closure.
Remember that it in Groovy can be used to reference the single argument of a closure.
Scala
map: Converts values from one value to another
flatMap: Converts values to collections of values and then concatenates the results together (similar to the flatten() method in Groovy)
filter: Limits the returned values, based on some Boolean expression
find: Returns the first value matching the given predicate
forAll: True only if all elements match the given predicate
exists: True if at least one element matches the given predicate
foldLeft: Reduces the values to one value using the given closure, starting at the last element and going left
foldRight: Same as foldLeft, but starting from the first value and going up (similar to reduce in Java)
Much like it in Groovy, in Scala, you can use the underscore to reference a single argument.
Summary
Functions as a first-class feature
Map, filter, reduce
Immutability and how it relates to FP
Various features supporting FPs in Java, Groovy, Scala, and JavaScript