Pure Functions in Software Development

A quick intro to pure functions and how they can help us have a good testable architecture

Catalin Patrascu
Level Up Coding

--

Photo by Ant Rozetsky on Unsplash

Whenever we think about pure vs. impure, we strive to get something as close to 100% pureness as possible.

Does software development have the same preference for purity?

What is a pure function in software development?

If we are to look it up on Wikipedia, we will notice that these pure functions have two properties:

  1. the return value of the function it’s always the same when called with the same arguments
  2. the function application does not have side effects

Hmm… let’s try to understand it better with some practical example:

int sum(int a, int b) {
return a + b;
}

Does sum function respect the two properties of pure functions?

Sure, for the same a and b values, we will always get the sum of the values, and also, whatever it does inside, it doesn’t mutate/change the state of the app/module.

Now let’s try with a counter example:

DateTime giveMeTheCurrentDate() {
return DateTime();
}

Let’s go through the same process? Does it violate any of the two properties of pure functions?

It does for the first one: each time we will call it, the result will be different since as much as we would love to, we cannot freeze the time 😭

And now the 1 million dollar question? If we cannot control it and have a deterministic outcome, how can we write a unit test or a test of any sort for it and have the insurance that everything works as expected?

Time to bring in some new tools and concepts:

  • dependency inversion principle
  • controlling the world

To put it simply, the dependency inversion principle states that we shouldn’t rely on concrete types but rather on abstractions.

Let’s see how this will look like in Flutter (abstract class is equivalent with interface in other languages - if it looks weird at first sight)

abstract class IDateManager {
DateTime retrieveDate();
}
class ConcreteDateManager implements IDateManager {
@override
DateTime retrieveDate() {
return DateTime.now();
}
}
class DateService {
DateTime giveMeTheCurrentDate(IDateManager manager) {
return manager.retrieveDate();
}
}

If we want to get the date with the concrete implementation, which is not a pure function we can call it like this:

final currentDate = DateService()
.giveMeTheCurrentDate(ConcreteDateManager());

How will a pure function look like in this scenario?

Let’s create another class for our unit tests that is deterministic:

class DummyDateManager implements IDateManager {
@override
DateTime retrieveDate() {
return DateTime.fromMicrosecondsSinceEpoch(1642167154);
}
}

Now, if we call the DateService with DummyDateManager, it will always produce the same result Friday, 14 January 2022, 13:32:34

Controlling our dependencies and environment empowers us to quickly write unit test cases and easily reuse parts of the codebase.

After all, when you write a piece of code, you are its creator; why shouldn’t you be able to control the time?

The second tool from our bag was controlling the world: our world. Without going into much specifics at the moment (because it will be covered in a later story), the idea is that all external nondeterministic dependencies like time, storage, network, device sensors can be instantiated by concrete implementation inside an Environment structure or a service locator that's available all across the app/module. It can look something like this:

void setupEnvironment() {
serviceLocator.registerFactory<IDateManager>(() => ConcreteDateManager());
}

And now, for using it inside DateService, we can use the service locator (ditching the abstract parameter manager):

class DateService {
DateTime giveMeTheCurrentDate() {
return serviceLocator<IDateManager>().retrieveDate();
}
}

Thank you for taking the time to read all the above, and I hope this story helped you level up just even for a tiny bit. Also, please don’t hesitate to spread the knowledge to other fellow developers.

--

--

iOS Engineer • Mobile Technologist 👨‍💻 Writes about building scalable & testable iOS apps