Functional Programming Can Bring You Orange Juice

By Boris Berkgaut  |  April 25, 2018

I would like to elaborate a bit on my colleague Marika Engström’s post “Learning Functional Programming Makes You a Better OO Developer” and give another example how functional approach helps building sound software systems.

 

One obvious difference between functional and and object-oriented approaches is how they deal with the state. In OOP, object state is said to be “incapsulated” inside objects. In pure object-oriented systems, where everything is said to be an object, object state consists of other objects, consisting of other objects, and so on, unless you reach  primitive objects like e.g. numbers, booleans, file handles, buffers, etc.

 

Functional programming is based on a different approach to composing systems. In any given context data (state) is clearly separated from behaviour (functions). Complex behaviours are composed from simpler ones using algebraic notions like function composition. Complex data types are composed from primitive ones also using algebraic notions (product types and sum types).

 

Pure object-oriented approach consider data as something to hide. Ideally, desired system functionality should arise from a well-orchestrated network of object interactions. Direct data manipulation is considered nothing short of a bad practice.

 

Functional approach, on the other hand, tends to destigmatize data and encourages style when composite data is manipulated by composed behaviours. This approach is very natural e.g. for describing how large quantities of data are being processed in distributed systems.

 

OK, let’s look at some code, finally! Let’s go with Java 8. Here is how one may compute an average (a pretty naive way):

 

public double average(List<Integer> list) {
   int sum   = list.stream().reduce(0, (acc, x) -> acc + x);
   int count = list.stream().reduce(0, (acc, x) -> acc + 1);
if (count > 0) {
      return (double) sum / count;
    } else {
       return 0.0;
   }
}

 

Of course, one do not want to go through the list twice, so instead of counting and accumulating in separate passes they would do a single pass. We will need a trivial class to keep state during list processing:

 

class State {
   public int sum   = 0;
   public int count = 0;
}

 

And then we can do the job in a single pass (this code is getting pretty close to how average is implemented in Java 8, modulo some performance tweaks):

public double average(List<Integer> list) {
   State finalState = list.stream().reduce(new State(),
  // accumulator
      (state, x) -> {
         state.sum   += x;
         state.count += 1;
         return state;
      },
  // combiner
      (leftState, rightState) -> {
         leftState.sum   += rightState.sum;
         leftState.count += rightState.count;
         return leftState;
      });
if (finalState.count > 0) {
      return (double) finalState.sum / finalState.count;
   } else {
      return 0.0;
   }
}

The second argument to reduce, called ‘accumulator’, is quite an interesting function: it takes an element of list and a state and produces an updated state. It looks like we are dealing with a really powerful abstraction here: a way to express stateful computation on a sequence of values (I would like to avoid discussion of the second function, ‘combiner’ in this post; it’s used for joining partial results in case of parallel processing).

 

We can go further and consider processing of unbounded streams (as opposed to bounded sequences) of values. This is actually done already by Java 8 stream abstraction! The notion of unbound streams in turn gives rise to two important possibilities: processing of arbitrary large data sets and event processing.

 

With streaming approach systems get organized in a pretty clean way: infrastructure for consuming values (or events) and state keeping gets separated from application-specific logic, the later expressed by pure functions which, as Marika stated, are “like school book examples of clean code”. By the way, “pure” does not mean “simple”. Some systems contain impressive bodies of domain knowledge expressed through pure functions (consider air traffic collision avoidance algorithm or loan interest calculation as examples). Testing such knowledge-rich pure components is just a pleasure because infrastructure is next to nonexistent and tests run so fast. It’s also much easier to apply advanced techniques like property-based testing or mutation testing to pure code.

 

In real world systems the state often needs to be something more complex than a couple of integers, for example a key-value store. In such cases it makes sense to apply separation of infrastructure from domain logic again: an impure function to fetch data from store and write results back, calling a pure function to transform next value and fetched stated into new state.

This is basically it. We looked at a very basic example and quickly developed a powerful composable abstraction useful for building practical systems in an easy to reason and efficient manner.

AUTHOR

Boris Berkgaut

Senior Software Engineer

You may also like

Go See Talents AB

Convendum

Västra Järnvägsgatan 3

111 64 Stockholm​

Sweden

  • Facebook - White Circle
  • LinkedIn - White Circle
  • Instagram - White Circle
  • Twitter - White Circle

Copyright © 2019 Go See Talents AB

We use cookies to give you the best user experience. By using this website, you agree to the use of cookies. Privacy Policy.