Ascending Functional Reactive Programming 3/7

Nicolas Roumiantzeff
Esker-Labs
Published in
12 min readSep 29, 2020
Photo by Simon

Why is Functional Programming so Controversial?

As seen in the first article of the series, simplicity or the lack of it is the thorn in the side of reactive programming. We will presently see why functional programming, despite all its superior features presented in the previous post, still lacks unanimity… as of today.

Part 1/7 Why is Reactive Programming so Complicated?
Part 2/7 Functional Programming to the Rescue?
Part 3/7 Why is Functional Programming so Controversial?
Part 4/7 Functional Reactive Programming: Simple and Unanimous?
Part 5/7 How did Functional Reactive Programming Originate?
Part 6/7 Where is Functional Reactive Programming Now?
Part 7/7 Which Functional Reactive Programming is the Grail?

1. The functional programming curse

Since a pure function has no side effect, a program written using functional programming cannot output information to the outside (display, logs, file system, database, connection). In one word, useless!

Since a pure function is context independent, a program written using functional programming will not take inputs from the outside (keyboard, mouse, file system, database, connection). In one word, useless!

Gladly, various artifacts exist to circumvent this serious limitation…

1.1. Monads to the rescue

I promised in a previous post I would talk about Monads. Now is the right time.

The brilliant idea to circumvent the functional programming curse is to repel the inputs and outputs to the border of the pure functional part of the program, letting the dirty work to the non-pure functional part.

Basically, the return value of the main pure function is a special function. This function is called with the outside-world inputs as arguments and its returned value as outputs to the outside-world.

This special function (or object using the object-oriented formalism) is typically a Monad. The name “Monad” comes from the category theory.

To explain in simple words what a Monad really is (or other complex variations of the same complex concept), a Monad is a description of the dirty work to be done. Here is a JavaScript illustration of the IO Monad in which inputs and outputs are expressed as functions to be compared to the corresponding classical code with side-effects.

It is amusing to note that some declarative programming enthusiasts adulate Monads because they allow writing procedural code, with pride.

1.2. Real world as argument to a pure function and as its returned value

As this may seem a pure utopia at best or a pure non-sense at worst, I will illustrate this idea in the case of a Web application:

A pure function, instead of modifying the DOM as a side-effect, takes an HTML page as input and returns the updated HTML page.

Should I care or should I not?

In one of the follow-up post, you will see that several significant reactive programming initiatives have implemented these concepts.

2. Performance

Functional programming is generally considered less efficient than the equivalent standard programming.

This is explained by the fact that a pure function cannot modify an external data structure so it must return a modified copy. There is no in-place quick-sort algorithm equivalent for instance. Have I talked about immutability already? I certainly did!

Another source of performance loss is the numerous additional function calls and dynamic function definitions in typical functional programs compared to their procedural counterparts. As of current JavaScript engine implementation, this is noticeable for large arrays. Here are two different scripts computing the sum of values contained in an array:

// functional style: a function is called for each array item
var sum = array.reduce(function(sum, item){
return sum + item.value;
}, 0);
// procedural style: no function call
var sum = 0;
var length = array.length;
for(var index = 0; index < length; index++){
sum += array[index].value;
}

Yet another source of potential performance issue is the garbage collector, yet another source of debate, related but not limited to functional programming. To collect or not to collect, that is the question!

Should I care or should I not?

In the context of a well-designed browser-side application, performance is usually a non-issue, the run time being negligible compared to the asynchronous delays of a typical web application.

Optimization techniques may be applied specifically for pure functions (memoization, referential equality, structural sharing, …) and some say that performance may even be superior to non-functional programming.

No one forbids a pure functional language to use a non-pure implementation. In any case, not me!

Keep in mind that some classes of problems have orders of magnitude faster algorithm solutions than pure functional ones… until someone really smart comes up with a really smart pure functional solution. For instance, it took 20 years of Haskell for a time-optimized pure functional implementation of the Eratosthenes prime number sieve and 10 more years for a space-optimized one. I can hear the ancient Greek geographer and mathematician laughing out loud in his grave!

3. Recursion

Beside parsing large collections, functional programming generates a lot of function calls for recursion.

Functional programming guys hate iterations because they are incompatible with immutability (seen in the previous episode) and they are fond of recursions because they are declarative instead of procedural (also seen in the previous episode). For example: factorial n is n times factorial n minus one.

On the contrary, a good practice in old-style procedural programming is to avoid recursions and favor iterations. Why is that?

3.1. Stack overflow

Who uses recursions and has never seen the stack overflow error message, raise your hand!

Even after getting rid of obvious stack overflow bugs, recursion consumes the call stack. In addition to performance impacts, it may feel very uncomfortable.

3.2. Determinism

There is a paradox in using recursion in functional programming.

On the one hand, functional programming ensures context-independence, meaning that the outcome of a function call only depends on its arguments, and on the other hand, you may get a stack overflow exception depending on the maximum size of the call stack, typically with big arguments.

This is very frustrating. No matter how many tests you perform on your recursive implementation, there will always be values of the arguments that might fail.

3.3. Lazy vs eager evaluation

For most functional supporters, a lazy programming language is a must while an eager one cannot be considered as truly functional . I wonder why most procedural supporters do not care at all. Probably because they never heard about the difference.

Although this battlefield is much wider than recursion, this is where lazy vs eager evaluation finally hit me, with an infinite loop,

By the way, we should say “strict”, “non-strict” and “reduction” since “eager”, “lazy” and “evaluation” are too implementation-oriented.

3.4. Control flow

To circumvent the recursion stack-overflow issue, several clever control flow techniques exist. Unfortunately, it seems to me that these techniques tend to divide rather than unite programmers.

A technique called Tail Call Optimization can prevent recursions from eating up the call stack. Amazing!
ES6 standard specifies Tail Call Optimization for JavaScript (referred to as Proper Tail Call). Exciting!
Google Chrome V8 JavaScript engine has implemented Tail Call Optimization. Wonderful!
Alas, Google Chrome developers removed Tail Call Optimization from V8. Read all about it here.

If the language itself does not support Tail Call Optimization, you might end up doing it by yourself, typically using trampolining. By the way, a trampoline function is an iterative, procedural function. Everything but a pure function.

Beyond trampolining, Continuation-Passing Style may be seen as an extraordinary advanced technique or as an extraordinary relapse. In terms of functional programming, going back to the future before structured programming was invented seems a little crazy . Any goto worshiper here?

Should I care or should I not?

Recursion brings joy. Recursion brings anger. Don’t be afraid to catch feels!

4. The infamous ternary operator

In the previous paragraphs we attempted to get rid of iterations (the for and while statements). In this one we will do the same for alternatives (the if and switch statements).

As an example, here is a function to compute the (positive) distance of two numbers using the ternary operator:

function distance(x, y){
return (x > y)? (x - y): (y - x);
}

The operator is said “ternary” because there are three operands, “x > y”, “x — y” and “y — x”, the first one being a Boolean value. Note that this operator requires two separate symbols “?” and “:” to separate the three operands.

Opponents to the ternary operator say its syntax is complicated and that you can easily get bitten by the language precedence rules unless you add paranoid parentheses everywhere (like I did in above example).

So, why are the proponents of functional programming so fond of the infamous ternary operator? Neither the if nor the switch statement by itself breaks function purity so what is this hostility all about?

Here is the same function naively implemented using if statements :

function distance(x, y){
var maximum = y;
if(x > y){
maximum = x; // impure: changing the value of a variable
}
var minimum = x;
if(x > y){
minimum = y; // impure: changing the value of a variable
}
return maximum - minimum;
}

This implementation is not pure in the functional programming sense because of the mutations. The only way to use if or switch statements in a functional programming style is to return a value inside the statement like in this implementation:

function distance(x, y){
if(x > y){
return x - y; // return in the middle of the function
}
return y - x;
}

Should I care or should I not?

Replacing each if and switch statements in a JavaScript program by the ternary operator might seem extreme and is reasonably subject to controversy.

Using return inside if or while statements leads to yet another controversy: single exit point vs early exit.

5. Inversion of control

Inversion of control is a profound idea which has a huge field of applications no matter what programming paradigm you use, object-oriented programming, functional programming or reactive programming. However, as a follow-up of the previous chapter, I will focus on getting rid of the ternary operator.

The following JavaScript code snippets implement a toy TLS handshake negotiation phase:

// Without inversion of control tcpConnection7 is computed in
// two alternative ways, with or without client authentication.
function tlsHandshakeNegotiationPhase(tcpConnection, clientCertificate){
var tcpConnection1 = sendClientHello(tcpConnection);
var tcpConnection2 = receiveServerHello(tcpConnection1);
var tcpConnection3 = receiveCertificate(tcpConnection2);
var tcpConnection4 = receiveServerKeyExchange(tcpConnection3);
var tcpConnection5 = receiveCertificateRequest(tcpConnection4);
var tcpConnection6 = receiveServerHelloDone(tcpConnection5);
var tcpConnection7 = clientCertificate? sendClientCertificate(tcpConnection6, clientCertificate): tcpConnection6;
var tcpConnection8 = sendClientKeyExchange(tcpConnection7);
var tcpConnection9 = sendClientHelloDone(tcpConnection8);
return tcpConnection9;
}
// With inversion of control tcpConnection7 is computed in
// a single way calling the clientAuthent function argument.
// The clientAuthent function might send a client certificate
// (client authentication) or not (no client authentication).
function tlsHandshakeNegotiationPhase(tcpConnection, clientAuthent){
var tcpConnection1 = sendClientHello(tcpConnection);
var tcpConnection2 = receiveServerHello(tcpConnection1);
var tcpConnection3 = receiveCertificate(tcpConnection2);
var tcpConnection4 = receiveServerKeyExchange(tcpConnection3);
var tcpConnection5 = receiveCertificateRequest(tcpConnection4);
var tcpConnection6 = receiveServerHelloDone(tcpConnection5);
var tcpConnection7 = clientAuthent(tcpConnection6);
var tcpConnection8 = sendClientKeyExchange(tcpConnection7);
var tcpConnection9 = sendClientHelloDone(tcpConnection8);
return tcpConnection9;
}

Should I care or should I not?

Unit testing functions without any alternatives in their implementation is so cool!

On the other hand, pushing all the logic up the top of the function call stack might lead to a cure that is worse than the disease!

6. Readability

I recently heard an expression that delighted me: “write-only code”. It is not specific to functional programming although it suits particularly well.

You will see what I mean with the following example, of my own device, in order to embarrass no one but myself. Here is my mostly adequate implementation, in a purely functional way (without classes nor objects), of the containers from the truly enlightening Mostly Adequate Guide to functional programming, just for your pleasure:

function Type(name, combinators){
const type = (function definition(implementation, subtype){
return function value(combinator, recombinators){
return combinators(name, combinators, type, definition, combinator, recombinators, value, implementation, subtype);
};
})();
return type;
}

Should I care or should I not?

Write-only code may be written in any programming language and with any programming paradigm. It mostly depend on the programmer!

Keep in mind that the one who will be reading the code you are writing today is very likely yourself!

7. Function declaration

7.1. Function expression vs. function declaration

When looking at posts about JavaScript functional programming, I see a lot of function expressions. For instance:

const compose = (f, g) => x => f(g(x));

Here is the function declaration alternative:

function compose(f, g){
return function(x){
return f(g(x));
};
}

In addition to being more verbose, the function declaration syntax cannot be used to declare functions returned by higher-order functions. For example:

const h = compose(f, g);

On the other hand, using function declarations, functions are hoisted. It means that a call to a function may appear in the script before its declaration. This is not the case with function expressions.

Should I care or should I not?

There is a paradox:

The function expression syntax, typically used in the functional programming style, seems more procedural. You first define variable compose, then you define variables f and g, and then you define variable h. If you change the order in this procedure, it fails.

Whereas the standard function declaration syntax is, well, declarative. The functions exist, at all times.

7.2. Anonymous function vs. named function

JavaScript, like many languages supports both anonymous and named functions. Nice! A new subject of discord!

Should I care or should I not?

Functional programming typically gives preference to anonymous functions especially when used as input to higher-order functions, avoiding global or scope namespace pollution.

Maintainability, on the contrary, greatly benefits from using named functions.

8. Object-oriented programming all-mighty

Listing technical reasons explaining the lack of unanimity for functional programming was important but the main explanation is cultural. Video killed the radio stars!

While functional programming bans side-effects and abides by context-independence, object-oriented programming promotes side-effects (in so-called “setter” methods for instance) and mocks context-independence (in so-called “getter” methods for instance).

When functional programming models mathematics, object-oriented programming models the real world. Do objects in the real world have changing state? Yes, they do! Are objects in the real world independent of the context? No, they are not!

Should I care or should I not?

Although object-oriented programming and functional programming may be seen as opposite paradigms, the truth is subtler.

Some say that both object-oriented and pure-functional approaches are theoretically equivalent. If you like zen kōans, here is a good one about closures and objects equivalence!

A source of confusion is that in object-oriented programming not everything is an object and in functional programming not everything is a function. Find below two interesting examples.

An interface is a collection of methods supported by an object. In object-oriented programming, interfaces are very useful as they may be used for encapsulation, abstraction and as an alternative to multiple-inheritance. The functional equivalent of an interface in Haskell is a typeclass. Here is the declaration and implementation of a “Translation typeclass:

-- typeclass declaration
class Translation a where
translateToken :: a -> String → String
translatePhrase :: a -> String → String
-- typeclass implementation
instance Translation Spanish where
translateToken = spanishTranslateToken
translatePhrase = spanishTranslatePhrase

A namespace, in the context of object-oriented programming, is a public constant static member object. Namespaces are very useful for structuring, organizing, decoupling and resolving name conflicts. Namespaces are not fully supported by all functional languages, such as Haskell. Here is a standard namespace in C# DotNet, an object-oriented language:

System.Collections.Generic

9. Debugging

Many functional programming advocates claim that they never do debugging. Is that because they never make any bug in their program or because debugging a functional program is a nightmare?

From my own experience, I list here three debugging functional programs issues (with limited details in order not to distract the reader):

First, in typical functional-style programs, functions often return the result of other function calls. To display the result of the first function in the debugger, you need to step inside the second function to display its result. This process goes on and on while never giving you a chance of displaying any returned values!

function f1(a1, b1, c1) {
return f2(f3(a1), f4(b1), f5(c1));
}
function f2(a2, b2, c2) {
return f6(f7(a2), f8(b2), f9(c2));
}
//...

Second, step-by-step debugging usually helps me analyze what is going on in a program. Unfortunately, step-by-step programming is by essence a procedural endeavor. It is often not be well suited for declarative programming.

Third, debugging deep complicated recursions is a real nightmare. Functional programming heavily promotes recursions. I let you draw your own conclusion.

Should I care or should I not?

Most of functional-style code you write will just work right away, maybe after fixing obvious syntax errors (likewise for procedural-style code, you may argue). Debugging is useful for those rare, albeit really nasty bugs.

When debugging functional programming code, you may fearlessly call functions in the debugger console to check the result, knowing that there will be no side-effect.

My quest continues with the next post in the series, Functional Reactive Programming: Simple and Unanimous?

Thanks
I would like to thank my cheerful teammates at Esker for bearing with my delirious ideas and exasperating theories.

--

--

Nicolas Roumiantzeff
Esker-Labs

Nicolas Roumiantzeff is a developer team member in one of 10 R&D scrum teams at Esker a French SaaS company. He likes music, JavaScript and planet Earth.