Functional Programming (Part 2): Pure Functions

Ahmad M. Hawwash
Bits and Pieces
Published in
5 min readMar 16, 2022

--

Photo by Miguel Á. Padriñán from Pexels

This article is a part of a series that talks about “Functional Programming”

In the previous article in this series we discussed one of the most important features in a programming language that supports Functional style; First-Class functions. In this article we’ll be talking about Pure functions. Which every FP concept should be built on.

Table of contents

  • What are pure functions
  • Characteristics of a pure function
  • Why impure functions are bad
  • Why pure functions are good
  • 4096-Cores
  • Honorship responsibility
  • Recap
  • Conclusion

What are pure functions

Simply, a pure function is a function that is deterministic and doesn’t produces side effects.

Characteristics of a pure function

Characteristic #1: ️Pure functions have to be Deterministic

A deterministic function is a function that for same input x output should always be the same y.

Examples on some in-deterministic functions:

  1. Math.random
const random = () => Math.random()

2. Date functions

const getDate = () => Date.now()

3. getUsers

const getUsers = await fetch('/users')

Those examples are considered indeterministic because for same input, output will differ.

(Note: getUsers function is indeterministic because users might have updated, there is not internet connection or the server might be down, or whatever)

Quick conclusion: determinism means that a function will never change its mind on the same input.

️Characteristic #2: A pure function must have no side effects

Where a side effect can be either an:

  • External dependency (access to external variables, I/O streams, reading/writing files or making http calls).
  • Or a mutation, (mutations on local/external variables or on passed reference arguments).

Yes, pure functions should be deterministic and shouldn’t produce any type of side effects.

And yes, it’s impossible to have stateful applications without side effects (querying a DB, doing an http call, reading user input or even displaying results on a UI).

But don’t worry, FP has some more concepts/fixes for that.

Examples on side effects:

1. isLessThanMin

With side effect (Impure):

let min = 60const isLessThanMin = value => value < min

Pure:

const isLessThanMin = (min, value) => value > min

Why impure? external dependency

Solution: dependency injection instead

2. squares

With side effect (Impure):

const squares = (nums) => {
for(let i = 0; i < nums.length; i++) nums[i] **= 2;
}

Pure:

const squares = (nums) => nums.map(num => num * num)

Why impure? imperative code that does mutations on original referenced array

Solution: use the functional .map instead. Which creates a new array at the end

3. updateUserAge

Impure:

const updateUserAge = (user, age) => {
user.age = age
}

Pure:

const updateUserAge = (user, age) => ({ ...user, age })

Why impure? mutation on passed user reference

Solution: refrain from mutating passed user reference, instead return a new object with new/updated properties

4. getFirst2Elements

With side effect (Impure):

const getFirst2Elements = (arr) => arr.splice(0, 2)

Pure:

const getFirst2Elements = (arr) => arr.slice(0, 2)

Why impure? mutation on passed arr reference using .splice

Solution: use the functional .slice method that doesn’t mutate the state of the array instead

Quick conclusion:

  • Pure functions are deterministic and have no side effects.
  • Deterministic: for same input x, output y should always be the same
  • Side effect: use of external dependency or variable mutations

Why Impure functions are bad

After taking a look on breaking the laws of pure functions, there are some obvious drawbacks:

  1. Makes functions tightly coupled with the surroundings
  2. Increases developer’s cognitive-load
  3. Induces state assumptions
  4. Increases developer’s codebase learning curve
  5. Race conditions
  6. Concurrency super enemy
  7. High unpredictability
  8. + All the lost benefits of having pure functions

Where each drawback might be -totally or partially- a result of another drawback.

Why pure functions are good

Due to pure functions rules, we have 2 major categories of improvements. On developer experience and on application performance:

On Developer Experience side (DX):

Taking the fact that our functions are now deterministic, independent and self-contained. Improvements will be obvious in:

  1. Predictability: elimination of external factors and changes of environment will make functions more sane and predictable.
  2. Maintainability: more predictable functions are easier to reason about. And that will reduce state assumption and developers’ cognitive-load.
  3. Composability: independence of functions and communicating only through input and output, that will allow us to compose functions easily.
  4. Testability: self-containment and independence of functions will take testability to the moon.

In terms of Application Performance:

  1. Cache-ability (Memoization): determinism of functions will give us the ability to predict what the output is going to be through the input (since each input will have a defined output), then we can cache functions based on inputs.
  2. Parallelise-ability: since functions are now side-effects free and independent, they can be easily parallelised.

4096-cores CPUs

We’re living in an era of multi-cores machines. The average CPU in hands nowadays has 8-cores, each core is eagerly racing for your computer’s resources. All these cores would be utterly useless if software we build can’t coordinate between them and serve ready-to-consume resources.

As Uncle Bob asked:

How are you going to write reliable code with 4096 processors contending for the bus?

And the answer lies in pure functions (the beating heart of FP). Pure functions do not produce side effects, thus do not have concurrency issues. That is what makes FP the future. It will seamlessly handle the concurrency of our next 4096 cores CPU.

Honorship responsibility

In the recent years many mainstream languages, like C++, C#, and Java, have adopted the functional style of programming. These languages, however, do not enforce function purity. It is our responsibility to honor that.

Recap

  • Pure functions are Deterministic and don’t produce side effects
  • Determinism: for same input x, output should always be the same y
  • Side effects: any use of external dependencies or mutations on any variable.

Conclusion

Pure functions are living at the core of Functional programming. They are satisfied with a set of rules. The result of applying these rules has tremendous outcomes, which will lead to the future of computing.

Thanks a lot for taking time reading through this article. I’m cooking the next ones in the series. Please feedback me and let me know what you think in the comments about this article or the series.

This is an article in a series of articles talking about Functional Programming

In this series of articles we cover the main concepts of Functional Programming. At the end of the series, you’ll be able to tackle problems in a more functional approach.

This series discusses:

0. A Brief Comparison Between Programming Paradigms

  1. First Class functions
  2. Pure functions (this article)
  3. Currying
  4. Composition
  5. Functors
  6. Monads

Resources

  1. https://en.wikipedia.org/wiki/Pure_function
  2. https://blog.cleancoder.com/uncle-bob/2012/12/19/Three-Paradigms.html
  3. https://blog.ploeh.dk/2017/01/27/from-dependency-injection-to-dependency-rejection/
  4. https://nofluffjuststuff.com/magazine/2016/11/the_duality_of_pure_functions

--

--

twitter: @AhmadMHawwash. Frontend engineering consultant @Mirado Stockholm. With interest in JS, TS, FP, React, Nextjs, Clean Code, Clean Architecture…