Some explanations of functional programming, can involve a laundry list of “functional” characteristics.

They’ll often mention:

  • Immutable data,
  • Value types,
  • First class functions,
  • Tail call optimisation

Language features that aid functional programming.

… or they’ll mention:

  • Mapping,
  • Reducing,
  • Pipelining,
  • Recursion,
  • Currying
  • Higher order functions

Programming techniques used to write functional code.

… and maybe even mention:

  • Parallelisation,
  • Lazy evaluation
  • Determinism

Advantageous properties of functional programs.

But let’s for moment just ignore all that. Functional code is really only characterised by one very specific thing:

the absence of side effects.

It doesn’t rely on data outside the current function, it doesn’t change data that exists outside the current function, and it always evaluates to the same result, given the same input. Every other “functional” thing can be derived from this property. Btw we call this a pure function.

Example

To further our understanding, let’s look at a few code examples:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

namespace Functional
{
  internal class Article1
  {
    int accumulator = 0;
    internal int sum(int[] values)
    {
      foreach (int value in values) {
        accumulator += value;
      }
      return accumulator;
    }
  }

  class MainClass
  {
    public static void Main(string[] args)
    {
      Article1 art1 = new Article1();
      Console.WriteLine(art1.sum(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 })); 
      Console.WriteLine(art1.sum(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 })); 
    }
  }
}
OUTPUT : Inconsistent Sum & Product
45
90
C# doesn't support declaration of functions or variable at the global scope, hence in this example the sum function is defined within a class: **Article1.**
Java doesn't support declaration of functions or variables at the global scope, hence in this example the sum function is defined within a class: **Article1**.
PHP supports declaration of functions or variables at the global scope, however as a fail safe the scope of a variable is by default restricted to the context that it is defined in. We can however override the default behaviour using the `global` keyword.
Python supports declaration of functions or variables at the global scope, however as a fail safe the scope of a variable is by default restricted to the context that it is defined in. We can however override the default behaviour using the `global` keyword.
Ruby supports declaration of functions or variables at the global scope, however as a fail safe the scope of a variable is by default restricted to the context that it is defined in. We can however override the default behaviour using by prefixing a variable with the `$` character.
Rust is a very strict statically type language; so to make the sum function misbehave, you have to explicitly wrap all mutable global variables in unsafe block; basically you affirm the unsafe behaviour.

We defined the accumulator variable outside of sum function scope. The results as you can see are inconsistent, because by choosing to use a global accumulator variable results will be carried forward from one process to another. i.e. second process started with a value of 45 as opposed 0.

The is something that can easily trip up a beginner, but similarly more skilled programmers when objects are involved – which means this form of the sum function cannot be considered a pure function, because of the inconsistency.

Pure Function

Now let’s see what needs to change to make this a functional or pure version.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

namespace Functional
{
  internal class Article1
  {
    internal int sum(int[] values)
    {
      int accumulator = 0;
      foreach (int value in values) {
        accumulator += value;
      }
      return accumulator;
    }
  }

  class MainClass
  {
    public static void Main(string[] args)
    {
      Article1 art1 = new Article1();
      Console.WriteLine(art1.sum(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
      Console.WriteLine(art1.sum(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
    }
  }
}
OUTPUT : Consistent Sum & Product
45
45

The output statements now return the same answer. Basically the variable accumulator that we use to keep track of our accumulative total is now contained within the scope of the function, or more specifically the only external input that the function relies upon is it’s specified parameter: values.

This version is therefore considered to be a pure function:

The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program.

So how is this different from objects (OOP)?

An element is considered an Object if all three of the following statements hold true:

  • They Represent Identity: the property of objects that distinguishes them from other objects.
  • They Manage State Transitions
  • They Perform Side Effects
Meaning objects are the very opposite of `pure`; but that by itself is not a bad thing; there are parts of applications that are difficult to design without this type of behaviour.

The only current exceptions within typical OOP languages are value type structures e.g. Structs and Enumerators.

Definition Of Values Types

Value types always have the following behaviours:

  • They’re inert,
  • They’re isolated,
  • They’re interchangeable.

Values types are generally either copied on assignment or copy on write (compiler level for optimisation) i.e. Assignment shares the same values in memory, until a write operation is performed. Reference types in comparison are always shared on assignment, and have multiple owners. e.g. Objects

Which means value types are easy to reason about; their values are always consistent i.e. PURE

Some may argue that we can can create object types to behave in a similar way to value types, but it always takes far more effort to get there, plus under covers it still will be tracked i.t.o. it’s identity, and will therefore have none of the resource advantages that real value types offer. It is these advantages that are probably the single biggest driving force behind putting more value types into traditionally OOP languages like C# and Java.

Here’s a summary from Java’s Language team:

Object identity serves only to support mutability, where an object’s state can be mutated but remains the same intrinsic object. But many programming idioms do not require identity, and would benefit from not paying the memory footprint, locality, and optimisation penalties of identity. Despite significant attempts, JVMs are still poor at figuring out whether an object’s identity is significant to the program, and so must pessimistically support identity for many objects that don’t need it.

More detail: Even a nominally stateless object (with all final fields) must have its identity tracked at all times, even in highly optimised code, lest someone suddenly use it as a means of synchronisation. This inherent “statefulness” impedes many optimisations. Escape analysis techniques can sometimes mitigate some of these costs, but they are fragile in the face of code complexity, and break down almost completely with separate compilation. The root problem is identity.

So what does it mean?

The lack of real value types by itself is not a hindrance here. We can still implement specially designed classes as a substitute for value types; basically by ensuring that any instances of the class behave in accordance with the rules of a pure function i.e. no side effects.

Java, C# and PHP's String type is an example of this. In addition to this; Java, C# and PHP built their functional features to operate within the current constraints of the language, meaning even without strict value types, you will be able to take full advantages of the functional features.

Easy versus Simple

Functional programming may from the onset appear to be more difficult, however think for a moment about the difference between these two words: easy and simple.

  • Easy: Something familiar, close to hand: comparison with learning to touch-type; an initially difficult task which became immensely easier over time.
  • Simple: Involving fewer concerns, for example: functions like min and max are simple, whereas objects are not simple.

Functional Programming like OOP is not simple, but it can be easy.

The point here is that all Concepts have a Cost, but just like learning the Delegate pattern in OOP or learning to touch-type; these concepts once grounded have immense pay back.

…and that’s it for this article.

Thanks to Xennox for contributing the C++, JavaScript and Python code examples.