Perhaps this seems like a truism, but: functions are what add functionality to a program.
Everything your program does, it does with functions. Classes, as a concept in Object Oriented Programming, are largely organizational; that is, they work to constrain and restrict functionality to a given context, to limit where that function(ality) can be used. This can be extremely useful when breaking a problem down, and grokking each part – but it also limits the code you write (which can be a good thing, don’t get me wrong — it’s designed to limit scope).
By leaning on functions over objects, on the other hand, you give yourself (and other people who use your code!) more flexibility in the long run. This, too, can be both a blessing and a curse – too much flexibility and limitless possibility can cause confusion – but knowing how to use it will give you tools to write better code.
When I first started teaching (object oriented) programming, I described classes as nouns and functions as verbs. That is, a class or object has things that it does and things that it owns. A
Person object might have related objects (properties) called
age, and might be able to do things like
This way of thinking can be genuinely useful when programming. For the example above it works really well: all of us can clearly imagine how to model a Person by describing things they have or do. Unfortunately, however, it can also cause some strange anti-patterns, especially when we get into real world programming scenarios.
I’m a mobile app developer, so let me take an example that happens in almost every app I work on: displaying lists of things.
In iOS, Apple has built a UI element called
UITableView that we as developers can use to display lists. This class allows you to handle various user-driven events (like if the user taps on an item in the list or scrolls to the next item) by providing it with something called a
UITableViewDelegate. This delegate has a bunch of things it can handle, but let’s concentrate on tapping and scrolling in particular, as those relate to two of the most common list-related tasks:
- taking the user to a new screen that displays details about the clicked on item, and
- loading more items when the user scrolls to the bottom of the list.
The Object Oriented (or OO for short) approach to this is to create a
UITableViewDelegate that does BOTH of those things – and for simple use cases, this isn’t the worst thing. In Apple’s example code, the
UITableViewDelegate lives in a class that is capable of doing both, and, what’s more, in simple cases both of those things are a line or two of code.
But if you’ve worked on programming for any amount of time, you know that simple things quickly become complicated. One line of code becomes two, becomes four, becomes a hundred.
Housing these two distinct bits of functionality in one place will make experienced programmers wince for a different reason: it violates one of our most important, most sacred, and (probably) most violated laws, the revered “Single Responsibility Principle” (or SRP for short). If you’re not familiar with it, the concept is simple enough: a class should do only one thing. This is a little vague, so we generally alter it to say: a class should have only one reason to change. In this case, if we wanted to change what happens when a user taps on a list item, OR if we wanted to change when we fetch the next chunk of items, this class would have to change; therefore, SRP is being violated.
So how could we fix that? The pure OO approach might be to give the
UITableViewDelegate an object that can display the item details screen as well as a separate object that can fetch more items. And that would work! But, I’d argue, it may also be overkill. In iOS there already exist classes to display screens, but they do more than just display screens; so handing one of these over to an instance of
UITableViewDelegate gives it access to more functionality than just displaying the screen. On the other hand, if you want to restrict
UITableViewDelegate to just displaying the new screen, you’d have to create a new class that does only that… which is reinventing the wheel, since you’ll need to copy the functionality from an existing class. A similar argument can be made for fetching more items.
In many modern languages, however, you can pass functions around as objects. That is, you can create variables/properties that hold functions. With this in mind, instead of creating a version of
UITableViewDelegate that accepts an object with the relevant function, you can create a
UITableViewDelegate which can just accept the relevant function itself, as an object.
This will allow you to share exactly the functionality you want with UITableViewDelegate, all the while keeping the rest of the functionality cordoned away.
By doing this, your code becomes more expressive and concise. Since we name functions using verbs, it becomes really clear what we want to do:
delegate.onItemSelected = viewController.displayDetailsScreen delegate.onScrolledToBottom = Api.items.loadNextPage
This feels much better. We’re clearly, concisely saying that when an item is selected, we should display the details screen; when the user scrolls to the bottom, we should load the next page of items. Better, these functions live in relevant places: the view controller (or, in best practice, the flow coordinator) should control displaying a new view, while the API should probably be in charge of fetching another page of items for display. And finally, this is all more reusable: a delegate with settable functions in this way can be used anywhere in your app, you just have to swap out the functions; the OO version would only be usable in one place.
This is the power of functional programming. By focusing on functions instead of objects, you allow yourself to let functions live where they make the most sense, not where they have to in order to satisfy some generalized requirements. It makes functionality easier to find, and easier to reuse. SRP is easier to satisfy, we’re following Apple’s recommendation to use configuration over customization, and jeez is this code readable – not to mention that due to its simplicity, it’s easy to test.
The concepts from FP have really leveled up my development. I’m writing more tests, I’m reusing a huge percentage of code between projects, and making changes to apps is a breeze. For sure, it’s a paradigm shift, and as a result it takes some work to grok – but once you do, so many more things become possible.