2 Comments

Functional Primer, Part 3 – When and When Not to Use It

Much of the power of functional programming is self-evident, but I wanted to spend some time exploring the reasons for and against using a functional style.

Five Drawbacks/Downsides of Functional Programming

I’m not trying to sell you some cure-all to your programming woes. There are definitely critiques of this approach and places not to use functional programming. Let’s look at a few situations that strike me as the most important.

1. Input/output (IO)

IO relies on side effects, so it’s inherently non-functional. But as it turns out, it’s also crucial to writing a useful application. There are a number of approaches for integrating IO into your application.

I think the best way to approach IO with functional code is to isolate the IO and accept that it won’t be functional. If you correctly isolate it, you can compose it with functional code and treat the results of the IO as a value. There’s a lot more we could go into about using IO in functional applications, but it’s almost impossible to handle as purely functional.

2. Recursion

Generally touted as one of the best parts of functional programming, recursion is also very expensive to use. It often utilizes the call stack as a data structure, but it turns out that memory is a valuable resource. There are ways to write recursive functions that don’t have such a high cost in terms of memory, but they’ll either require compiler optimizations or rewriting the recursive function in a slightly different style.

3. Terminology problems

Functional programming has a lot of things going for it, but being easy to explain is not one of them. With its roots in mathematics, it can be bogged down by a lot of terminology. Terms like “pure functions” and “referential transparency” can sound like Greek to those who are new to functional programming, or even to functional veterans who haven’t learned all of the terms.

This might sound trivial, but I don’t think the problem should be understated. I’ve had conversations with coworkers where we spend half the time defining what we’re saying, only to realize we’re saying the exact same thing.

4. The non-functionality of computers

This doesn’t mean that computers don’t work, but that computers don’t operate in a functional way. They’re built on stateful, imperative operations. When it gets down to the metal, they are just large state machines, so the idea of a pure functional language all the way down to metal doesn’t make sense.

5. The difficulty of stateful programming

Expanding on what I mentioned just a moment ago, representing state in functional programming isn’t as intuitive as most other functional operations. There are a number of ways to work around state representation in functional programming, but they’re never quite as direct as in a non-functional language.

Redux.js is a great example of how to manage state in a functional way. Its biggest downsides are having to use a lot of boilerplate and a lot of terminology.

Four Strengths of Functional Programming

Of course, functional programming has its advantages, too.

1. Abstraction is powerful

The power of abstraction offered by functional programming is amazingly strong. If we write a bunch of pure functions, we get to leverage referential transparency to abstract away and hide complexity. We can start to blur the line between the results of a function call and a plain value. At the end of the day, it doesn’t really matter which it is, as long as you know what the value is supposed to be.

Abstraction is the greatest strength of functional programming. You get to rely on how your functions act, so you can zoom in on the problem and solve it one function at a time.

2. It’s inherently parallel

It might not be immediately apparent, but functional programming lends itself well to parallel programming. The simplest example to demonstrate this is to imagine pairing up a map and a reduce. When you map across a list, you could distribute each call of the function to a different thread/process/computer. Then you could have a separate thread/process/computer handle calling reduce across the results, or even set up multiple threads/processes/computers with each working on a section of the reduction.

3. It’s easily testable/debuggable

The most salient argument in favor of functional programming is how easy it makes testing and debugging code. If we end up with a bunch of functional code, all of the functions should be totally isolated. That vastly simplifies testing and debugging.

Finding a bug becomes much more direct when you can clearly define the inputs and outputs of a function. With clear isolation for each function, it also becomes a lot easier to pass in only the dependencies you need for testing. This isolation offers more insight into what’s breaking and requires less time spent testing a component of your system repeatedly across multiple dependent tests.

4. Development is faster

This reason is influenced by my personal opinion and experience, but I believe that functional programming leads to a faster development experience. Functional programming pushes you to break down your problem into smaller composable actions. I find it easier to complete each of these actions in series, instead of in parallel. You can more easily define an action as “done” before moving on to the next one.

Conclusion

When all is said and done, I think the strengths of functional programming outweigh the weaknesses. Functional programming makes a lot of sense for much of general-purpose programming, but you do need to be careful about its limitations in the areas of state management and input/output.

References


This is the third post in a series on Functional Programming:

  1. What Is Functional Programming?
  2. Ideas & Patterns
  3. When & When Not to Use It
  4. Ideas for Practicing & Exploring