Applicative Functors
Applicative functors, or simply applicatives, are a powerful concept in functional programming. They extend the capabilities of functors, which are structures that can be mapped over. Applicatives allow you to apply functions to values inside a context, such as Optional
, Stream
, or CompletableFuture
.
The key characteristic of applicatives is that they provide a way to combine multiple values within a context using a function. This is achieved through the ap
(apply) method, which takes a function wrapped in the context and applies it to a value wrapped in the same context.
Here’s an example of using an applicative with Optional
:
Optional<Integer> x = Optional.of(5);
Optional<Integer> y = Optional.of(3);
Optional<BiFunction<Integer, Integer, Integer>> sum = Optional.of((a, b) -> a + b);
Optional<Integer> result = sum.flatMap(f -> x.flatMap(a -> y.map(b -> f.apply(a, b))));
// result = Optional[8]
In this example, we have two Optional
values, x
and y
, and a function sum
wrapped in an Optional
. We use flatMap
and map
to apply the sum
function to the values inside the Optional
context, resulting in a new Optional
containing the sum.
Function Composition
Function composition is a fundamental concept in functional programming. It allows you to combine multiple functions together to create a new function. In Java, you can achieve function composition using the compose
and andThen
methods provided by the Function
interface.
Here’s an example of function composition:
Function<Integer, Integer> multiplyByTwo = x -> x * 2;
Function<Integer, Integer> addThree = x -> x + 3;
Function<Integer, Integer> composedFunction = multiplyByTwo.compose(addThree);
int result = composedFunction.apply(5);
// result = 16
In this example, we have two functions, multiplyByTwo
and addThree
. We use the compose
method to create a new function composedFunction
, which first applies addThree
and then applies multiplyByTwo
to the result. Applying composedFunction
to the value 5 yields the result 16.
Monads
Monads are another powerful concept in functional programming. They provide a way to chain operations together while abstracting away the details of the underlying computation. In Java, examples of monads include Optional
, Stream
, and CompletableFuture
.
Monads are defined by three key operations: of
(or unit
), map
, and flatMap
(or bind
). The of
operation wraps a value inside the monad, map
applies a function to the value inside the monad, and flatMap
allows you to chain monadic operations together.
Here’s an example of using monads with Optional
:
Optional<String> name = Optional.of("Alice");
Optional<Integer> age = Optional.of(25);
Optional<String> greeting = name.flatMap(n ->
age.map(a -> "Hello, " + n + "! You are " + a + " years old."));
// greeting = Optional["Hello, Alice! You are 25 years old."]
In this example, we have two Optional
values, name
and age
. We use flatMap
and map
to combine the values inside the Optional
context and create a greeting string. The resulting Optional
contains the greeting message.