The last week of May, the beautiful city of Barcelona was hosting an annual fair for all the IT enthusiasts, programmers and software companies – the fifth edition of JBCNConf. This year, the three days were filled up with an enormous number of 64 talks and 10 workshops, all divided into 5 parallel tracks and brought to us by over 80 specialists.

The topics varied, from highly technical, unveiling the quirks and features of Java or Kotlin, through cutting-edge development operations, like deployments, analytics, auto-scaling, up to established best practices in architecture, as well as work organization, productivity techniques and many, many more!

Of course, we could not miss this great event ourselves, this year the Apiumhub was present in the conference on all of the fronts: as sponsors, as speakers and as attendees.

 

Global Software Architecture Summit

Today, I wish to share with you some notes after the talk that caught my greatest attention and probably influenced me the most during the whole conference. It was the presentation given by Mario Fusco. Mario is a well known figure in the programming community, a speaker, mentor, conference organizer, writer and a devoted Red Hat developer.

The Pursuit of Pragmatic Programming

The topic was: From object oriented to functional and back: the pursuit of pragmatic programming. The premise of the talk is simple, don’t fight the Object-Oriented Programming paradigm nor don’t you fall blindly into Functional Programming style, be smart, be pragmatic, search the balance. In today’s world full of fake news, clickbait articles and opposing forces we are given a false sensation that only extreme choices exist, as if there were no middle way to take. As Mario states it, we live in a world of false dichotomies. Black or white, good vs bad, OOP vs FP, we see things as opposite and exclusive. And we have to choose between two, supposedly contradictory paths and no compromise is possible.

The reason may be, we are searching for simple solutions, too simple one may say. As if limiting ourselves to just the half of the available options would make our lives somehow better. Yet there is no need to choose at all and no reason to simply discard a great deal of useful knowledge in order to pursue some imaginary division. True that, sometimes, it really does make sense to pick one over the other, you probably do not want to start a project with two languages on the backend side or immediately connecting to all possible databases out there. Yet for the coding style, no such requirement exists.

Object-Oriented Programming

Object-Oriented is around since decades now, the idea is way older though. We may date the popularization of the term to 1994 and the publication of the Design Patterns book by “the Gang of Four”. We all know well how to set up our interfaces and implement against them, we have under control all ours factories, strategies, decorators and facades… However, the original idea of OOP was not meant to be “class-oriented”, as we currently do it, rather a “message-oriented” (between these objects). Similarly to the actor model pattern if you will. Nowadays, it is an established way and it’s here to stay, whether we like it or not. It works quite well and it solves the problems. It also generates new ones, but hey, does the functional way offer us a solution without a cost?

Functional Programming

Well it’s not as easy to define what the FP precisely is. It has something to do with monads and immutability. But is it really better? Since most modern languages support functions as first class citizens are all of them functional?

The easiest to say would be, as the OOP is objects with data and a public interfaces allowing us to manipulate that data, so the FP would be a set of composable functions operating on the immutable data models. Decouple the model from the behaviour, assure the data is immutable and functions are pure, and that’s basically it. One may say it’s not that very different one from the other. From the programmer’s perspective it’s just a reversed notation. Instead of calling “shoppingList.sum()” we call the “sum(shoppingList)”.

Polymorphism

The changing factor between FP and OOP is polymorphism. Which gives the possibility to define the objects’ behaviour and do not make a dependency between the caller and the callee. As we know there are two faces of the computation, the data on one side and the behaviour on the other. Natural for OOP is to create an interface which clearly declares the behaviour we want. Each class implementing the interface is independent and encapsulates it’s logic.

One little oversimplified example:

public interface Shape {
    double getArea();
    double getCircumference();
}

public class Rectangular implements Shape {
    private final int a;
    private final int b;

    public Rectangular(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public double getArea() { return a * b; }
    
    @Override
    public double getCircumference() { return 2 * (a + b); }
}

public class Circle implements Shape {
    private final int r;

    public Circle(int r) {
        this.r = r;
    }

    @Override
    public double getArea() {
        return Math.PI * r * r;
    }

    @Override
    public double getCircumference() {
        return 2 * Math.PI * r;
    }
}

On the other hand, in FP we define the data objects without behaviour and the functions which are the behaviour require to know the variety of implementations for every possible code branch. It makes them coupled to the data, since the functions have to know all possible models that may interact with them.

public interface Shape { }

public class Rectangular implements Shape {
    public final int a;
    public final int b;
    
    public Rectangular(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

public class Circle implements Shape {
    public final int r;
    
    public Circle(int r) {
        this.r = r;
    }
}

public static double getCircumference(Shape shape) {
    if (shape instanceof Circle) {
        return 2 * Math.PI * ((Circle) shape).r;
    }
    if (shape instanceof Rectangular) {
        return 2 * (((Rectangular) shape).a + ((Rectangular) shape).b);
    }
    throw new NotImplementedException();
}

public static double getArea(Shape shape) {
    if (shape instanceof Circle) {
        return Math.PI * ((Circle) shape).r * ((Circle) shape).r;
    }
    if (shape instanceof Rectangular) {
        return ((Rectangular) shape).a * ((Rectangular) shape).b;
    }
    throw new NotImplementedException();
}

The functions which define the behaviour do depend on the all implementations of the common interface. So there we have a small win for the OOP, yet we shouldn’t forget it’s just a specific case where polymorphism does the trick a little better. The FP in this case could make use of pattern matching and therefore make the code a little nicer and organized too, yet still dependent. We may easily find examples where Functional approach shines too. The lesson here is, to use the proper tool for the problem you need to solve.

Some more definitions

Later on, Mario gets into more details of how the both paradigms have to offer. Let’s focus for a while on few of them:

Composition – we may find composition in both worlds, but it means something different in each of them. The function composition, commonly used in FP paradigm borrows from the mathematics, where two functions are composable when:

f : X → Y and g : Y → Z if (g ∘ f )(x) = g(f(x))

Or in simple words when we may “glue the two together and get the third with exact properties”. In OOP word, the composition is used in the sense of composing objects into more sophisticated associations. Often with the help of some dependency injection mechanism, for the salvation or our despair…

Immutability – The great deal for Functional Programming, allowing us to simplify or even avoid a lot of pain points of Object-Oriented design. It gives us thread safe and easy parallelism since no race conditions are possible and no synchronization is required. Caching is easier, the data correctness and consistency is easy to achieve. Last, it provides better encapsulation. The downside may be a lower performance due to the necessity of constant recreation of the whole data objects, but there are tricks to solve this problem too.

Error management – FP popularized the monadic return declaration. Natural for OOP is to throw an error (if for example caught on dividing by 0). The FP here offers us the possibility to return an Optional, which has many benefits – may be memoized and does not break the execution flow, just to name some. According to Fusco, exceptions should be avoided and only thrown as a means of last resort for non recoverable errors. For all other cases it’s comparable to a global GOTO jump.

Imperative vs declarative – A small declarative function is most of the times more concise, cleaner and easier to read than a disorganized for-loop with it’s pointers and checks. It’s way more attractive, isn’t it? Yet let’s not forget that this superficial beauty and simplicity often does not stand strong when a change request comes in. The good-old imperative approach survives the request of change way better. The declarative often forces us to rebuild it from scratch.

Here’s a great eye-opening example given by Mario in his talk. Imagine you have a log file with tons of entries, now extract last 40 lines which contains “ERROR” inside. Try to do it both ways, imperative and declarative. Simple so far. And now, what would you have to do, to instead of picking just the error-line you would also need to take the one that came before?

Summary of the Pragmatic Programming talk

The Object-Oriented and Functional styles offer us very different approach to programming. Where the first offers polymorphism, the other gives us functional decomposition. OOP is by definition stateful, mutable and imperative in style, the functional offers statelessness, immutability and declarative way. What more there is to the OOP: exceptions, threads, statements and iterations; whereas for functional we have optionals and validations, futures, expressions and recursion.

The important lesson is to know and understand both worlds, be fluent in each style and be able to interchange them if necessary. The key to the good code is avoiding extremes, you probably do not need to go 100% functional, nor you shouldn’t avoid it at all costs. Seek for balance that works for you, whether it will be a well established object-oriented design with functional parts or functional skeleton manipulating bits of stateful objects. Take the best of both worlds, avoid the extremes, be smart, be pragmatic.

JBCNConf 2019 - Pragmatic Programming

Great thanks to Mario Fusco for his awesome work!