Skip to content

Functional Composition. A Deeper Dive

Posted on:August 20, 2023 at 01:13 PM

In our previous lecture, we took an in-depth look at method chaining, a powerful technique that involves executing functions in sequence, one after the other. We explored how chaining empowers us to streamline code execution and enhance code readability. Today, we’re venturing into a new territory: functional composition. This technique, while similar to chaining, operates in a reverse direction, and it’s designed to elegantly combine functions for powerful and expressive transformations. Let’s dive into the world of functional composition and see how it differs from chaining.

Table of contents

Open Table of contents

Sections

Distinguishing Composition from Chaining:

Before we plunge into functional composition, let’s quickly recap method chaining. In method chaining, the first function in the chain is executed first, followed by the subsequent functions, one after the other. This chain can extend as long as needed, simplifying code execution.

Now, let’s meet functional composition. While it shares similarities with chaining, it operates in reverse. In functional composition, the second function gets executed first, followed by the execution of the first function on the result produced by the second function. This technique allows us to elegantly combine functions and create a seamless flow of transformations.

Demonstrating Functional Composition:

To better understand functional composition, we’ll work with a practical example. For this purpose, we’ll define a custom functional interface and illustrate the concept using a scenario involving squares.

@FunctionalInterface
interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<V, T> before) {
        Objects.requireNonNull(before, "Before function cannot be null");
        return v -> this.apply(before.apply(v));
    }
}

class Square {
    int getArea() { return area; }
    void setArea(int area) { this.area = area; }
    private int area;
}

public class FunctionalCompositionExample {
    public static void main(String[] args) {
        Function<Square, Integer> fun1 = Square::getArea;
        Function<Integer, Double> fun2 = area -> Math.sqrt(area);

        Function<Square, Double> composed = fun2.compose(fun1);

        Square s = new Square();
        s.setArea(100);

        double side = composed.apply(s);
        System.out.println("Side of the square: " + side);
    }
}

In this example, we’ve defined a functional interface Function that facilitates functional composition. We have two functions, fun1 and fun2, each performing a specific transformation on their inputs. The real magic happens when we compose these functions using the ‘compose’ method.

The Art of Composition:

In functional composition, the composed function takes the output of the second function (fun2) and feeds it as input to the first function (fun1). The result is an elegant composition that directly transforms a square’s area into its side length.

Conclusion:

Functional composition presents us with a reverse order of execution, where the second function leads, followed by the first function. This technique allows for seamless and expressive combinations of functions, making complex transformations appear effortlessly simple. As you journey through the realm of functional programming, keep in mind that functional composition is a powerful tool that can help you craft elegant, efficient, and highly functional code.

You can find the repo for this section of the course Here