Pallet Town: First Steps to Functional

I’ve been taking a deeper look at Functional Reactive Programming in the form of RxJavaand ReactJS. While I am still at the early stages of really looking at this and forcing myself to actually work in these technologies that I’m still no really sure make a lot of sense for my use cases (more on that at a later date), I was taken aback by the simple usefulness of the “Functional” part of the equation. At the risk of grossly oversimplifying the benefits of functional programming (or at least adopting a more functional style in your object oriented code), there is a ton to be gained with just a little bit of re-thinking of how you think about the relationships between your objects and how you structure your methods. This is because one of the main benefits of functional programming is that it minimizes state and therefore unintended side-effects. Here are the two quickest and easiest ways to improve your code today using a more functional mindset.

One Function == One Action:” Every function you write should do one thing and only one thing. Let’s take a very simple sample of what this should look like:

public Pokemon getPokemon(int id) {
	Pokemon  myPokemon = PokemonStore.getById(id);
	myPokemon.setType("fire");
	PokemonStore.save(myPokemon);
	return myPokemon
}

The Assumptions in that are that we have some PokemonStore class that is effectively an ORM for your Pokemon. That’s fine. The issue here is that our ‘getPokemon’ function doesn’t just fetch us the correct record by id as you would expect it to. Instead it updates it ‘type’ property and reserves the mutated record to the datastore before returning the record. This is obviously terrible on a number of levels but looking at it from a functional point of view, the primary sin is that it does three things (getting a record, mutating a record, and saving a record) rather than just one and what it does doesn’t match the method signature. A better version of this method would have mutating the type in another method and simply return the record:

public Pokemon getPokemon(int id) {
	Pokemon myPokemon = PokemonStore.getById(id);
	return myPokemon;
}

As you can see that’s easy to read, does one thing, and there’s little to know risk that a future developer won’t understand what the method is supposed to do.

Deterministic Outputs: A method given the same inputs should always return the same outputs. If that’s not true, then you likely have too much coupled state in your code that is causing unforeseen issues. This is a great example of what not to do:

public Pokemon getFirstPokemonInRoster() {
	List<Pokemon> roster = User.CurrentUser().getRoster();
	Pokemon starter = roster.get(0);
	return starter;
}

There’s a lot not to like in this method. For starters it relies on the start of a Users.CurrentUser() singleton, which instantly creates couple state. Another issue is that it’s not very flexible — is it really true that we will only ever care about the current user’s roster? Finally it’s not written in a way to be optimized for easy BDD / testing and violates the functional principle (or perhaps guideline) of passing in the objects / values a method needs to run into the method directly rather than pulling them from another place. Here’s a minor change that addresses all of those issues:

public Pokemon getFirstPokemonInRoster(int userId) {
	User user = UserStore.getById(userId);
	List<Pokemon> roster = user.getRoster();
	Pokemon starter = roster.get(0);
	return starter;
}

As you can see, the revised method can now the pull the roster for any arbitrary user and does not rely on a user singleton. You can easily imagine a test being written that passes in a valid user id to test the golden path for this and also another test with an invalid value for the userId paramater; incidentally, my method still doesn’t handle that case, but there are multiple ways you might do that and catching that sort of thing is really what automated testing is for ;).

I hope you’ve found this helpful! If you have any questions or comments, please sound off below or on Twitter.