The power of functional programming in Javascript

Steven Ellis
ITNEXT
Published in
9 min readJan 22, 2019

--

Some years ago I made a simple coding mistake that set me on the path to gaining an appreciation of functional programming and its benefits. Whilst developing a Javascript application, I passed an array of key-value pairs to a function, with the intention of making some value changes to the elements of the passed-in array, and sending back a BRAND NEW array containing those changes.

However, instead of using the map function or some other way of generating a NEW array, I simply had the function make these changes to array sent in, and return the ORIGINAL array back to the caller.

In Javascript, primitive data types (string, number, boolean, null and undefined) are passed by value, and objects are passed by reference. This means primitive data types are immutable, and objects are mutable.

In my program, by mutating the original array, I changed the state of this array wherever else it was referenced in the program.

When I ran my code, I couldn’t understand why my original list of users in the application was changing unexpectedly, until a colleague pointed out what had happened.

Had the key paradigms of functional programming been followed, I would not have made this error. The goal of this post is to explain what is functional programming and why it’s a good paradigm to try to follow.

What is functional programming?

Functional programming is a paradigm that treats computation as the manipulation of value, and avoids changing state and mutable data.

In functional code, the output value of a function depends ONLY on the arguments that are passed to the function. In other words, calling a function f twice with the same value for an argument x produces the same result f(x) each time.

This is in contrast procedural and object-oriented programs, which can often depend on a local or global state, which may produce different results at different times when called with the same arguments but a different program state.

By eliminating such side effects, the functional program is made predictable, simple, easy-to-understand, loosely-coupled and extensible.

Let’s unpack state and mutability some more.

Mutable and Immutable

Immutable means state cannot be modified after it is created. Mutable means state can be modified after it is created. In Javascript, primitive data types are immutable. Any object (key-value pairs, arrays, functions, objects created with the ‘new’ keyword) is mutable in Javascript, and can be modified after it is created.

State

A program is described as stateful if it is designed to remember preceding events or user interactions; the remembered information is called the state of the system.

State thus tracks changes to variables and objects over time.

In programming, state is maintained by variables that are not among the arguments of the relevant function.

Object-oriented languages like C++ and Java make extensive use of state variables in the form of member variables inside classes and objects. Procedural languages like C typically use global variables declared outside the current scope to keep track of state.

In Javascript, however, this is discouraged. Maintaining state variables using mutable data would mean that functions will have hidden or unpredictable dependencies, which is at odds with javascript’s functional purity.

OK, but if maintaining state is a crucial requirement in your Javascript application, how is this achieved?

Fortunately, it is possible to keep track of state in a functionally pure way. We do so by passing the state information from one function to the next, thus making these dependencies explicit (instead of storing them implicitly in mutable data).

The functional approach is thus to return a new state from a previous state. The functions themselves do not hold any information, so they will always update state m to state n.

Pure functions can thereby be combined, to provide streams of events which explicitly model time without relying on outside state.

In object-oriented programming, a code OBJECT represents the state a domain object; it changes over time to reflect changes in that domain object.

In functional programming, a VALUE represents the state of a domain object; it never changes, you simply create different values to represent different states.

But how efficient is value-based programming?

At the back of your mind, perhaps you might be thinking that this paradigm (always returning a new value from a function and never mutating an existing one) is inefficient.

For instance, say I have a list of a million integers, and want to increment the tenth by one unit. Copying the entire list with a new number in its tenth position is wasteful.

You would be right, but it is only the conceptual way of describing the operation to the language compiler or interpreter. The compiler or interpreter is free to take the first list and just overwrite the tenth position.

The Javascript compiler is thus optimised to make pure functions efficient. Persistent data structures are designed to efficiently reuse previous structures, so that a change provides only a delta and not a completely new rendition.

By leaving the mutation details up to the compiler, things also become more efficient and less error prone. If the compiler can reason about the situation when many threads want to update the same list at different positions, it is up to the compiler to ensure that overwrites do not collide. If, by contrast, the operation is described as “go to this position and overwrite what you find”, then it is the programmer, not the compiler, who is in charge of making sure that overwrites do not collide. Leaving these kinds of operational details up to the programmer can lead to nasty bugs, race conditions and lack of concurrency.

OK, let’s see some visual representations of functional programming to make the concept clearer.

A flip book

In a flip book each page represents the world as it exists at a moment in time. Each subsequent page advances the story by slightly modifying the previous representation.

In a functional flip book program, there would be a function that accepts a flip book page as an argument and returns a new page.

In this manner, the story ‘unfolds’ without ever actually changing the state of existing objects. We simply supersede each page with a newer one. When we are done, we have a complete flip book, which can be flipped through backwards and forwards, because the function just added new pages to the book, in no way change the state of any of the existing pages in the book.

In this example, we generate a simple functional ASCII-art ‘flip book’ which is logged to the console.

A conveyor belt

In a conveyor belt or assembly line, a raw input is passed down the different steps on the belt, with each step adding to or modifying the item in some way and passing it onto the next function on the belt.

Functions can be thought of as steps taken to convert the raw input (the parts) into the processed good (the output.). The assembly line in functional programming builds new outputs while leaving the inputs intact, whereas the assembly line in traditional object-oriented programming transforms the input.

A simple functional ‘conveyor belt’ processor.

Thinking in terms of values

A good way to conceptualise and understand the benefit of functional programming is to think in terms of values.

In the world of functional programming, everything’s a value, and you apply functions to values (which may be functions) to produce new values, preferably without producing any side effects.

Rich Hickey, the author of Clojure (a functional programming language), sums up this concept in this excellent video.

In short, he explains that mutable objects are abstractions of PLACES, being either convenient representations of memory addresses or of tables/documents/records in durable storage.

He calls this paradigm place-oriented programming (PLOP), and explains how it was born of limitations of early computers where RAM and durable storage was expensive and in short supply, rendering place-centric implementation necessary.

In modern computing, these limitations no longer exist. Place is therefore no longer a concern and has NO relevance in an information model. It is only an implementation detail, and we shouldn’t elevate it to first-class citizen status. Rather, programs should only concern themselves with VALUES.

In the value-oriented programming model, values are immutable, easily shared and semantically transparent (you can see all of what it is).

If you ever encounter a value, you can just start using it because it’s immutable! This is the key of functional programming and higher order functions. You can share pervasively and you never need to worry for one moment that you can’t mess anyone else up and no-one can mess up you.

Some of the other awesome benefits of value-programming that Rich describes are:

Operations on values are stable (performing the same operation on the same value will always produce the same answer). This means easily reproducible results. Testing and debugging can therefore reproduce failures without needing to replicate state first.

Values are language independent, enabling polyglot programming. Places are defined by language constructs. So with places you don’t have a definition independent of your language that you can use as a basis for information sharing.

Values are generic. They consist of very few value types (lists, maps, sets, strings, numbers). This means they are easily represented in any language, and there are fundamental abstractions for their aggregation.

Values make the best interfaces. Data-driven interfaces are simple and easy to move around. They can also easily be enqueued. In place-oriented programming, interfaces are class-specific, meaning reuse not easy. Classes from different namespaces are not interoperable.

Places are tightly coupled to application, language and program flow. Values aggregate to values. This means you can add, assimilate and composite values. All these benefits accrue to compositions too. By contrast, there is no benefit to combining or composing places.

Values are easy to pass values around, worry free. To share places you need to send around a reference to a thing that can change (it’s mutable). We need only look at the internet to see this is practice: all data sent from client to server vis HTTP are values (JSON, text, blobs), NOT objects.

Remembering is easy with values: just make a copy. Remembering is difficult with places because it’s mutable so will change over its lifetime. Values are stable. Places are not.

Functional programming and Javascript libraries / frameworks.

The world’s two most popular client Javascript libraries/frameworks are React and Angular. Both solve the difficult problem of reactive programming, that is, efficiently updating the view or user-interface to changes in the state of the application.

React devised an architecture using the paradigm of components, state and props.

Angular decided to use RxJS as a key component of its MVC architecture.

Both, however, require state to be maintained through the use of pure functions and immutable state.

‘All React components must act like pure functions with respect to their props.’

‘What makes RxJS powerful is its ability to produce values using pure functions. That means your code is less prone to errors.’

References

--

--

Computer scientist, currently also pursuing a Masters in Data Science.