What You Should Know About Functional Programming
A few years after Brian Lonsdorf first started coding, he was struggling to keep his code readable and maintainable. So he started looking for a good way of managing that.
“Boy, is it easy to make a mess,” he said. “It’s really hard to write maintainable software.”
Lonsdorf was part of the “Rails generation,” coding in the object-oriented language Ruby. He found that learning paradigms such as object-oriented programming principles and domain-driven design were helpful for keeping his code in order, but he soon hit a wall using those principles as well.
“I started to second guess the whole object-oriented thing, because I was doing it and really trying hard to follow it — it just was not panning out the way I wanted it to,” Lonsdorf said. “I was still writing applications that didn’t turn out to be clean and readable.”
What Is Functional Programming?
Although functional programming results in cleaner code for those who understand how functional code works, it can be difficult for uninitiated developers to understand its structures and conventions.
“I found that most people I knew were like: ‘Who would use this code? I can’t read it,’” Lonsdorf said. “So I thought I’d write a book because I really believed in it, but there was no community that could share the knowledge, and I thought that would be valuable.”
How Functional Programming Works
Ian Mundy describes the difference well in a codeburst article: declarative programming is like asking someone to paint a landscape, and imperative programming is asking them to paint a landscape by following along with a Bob Ross video. Imperative programs tell the computer exactly how to execute what you want it to do, while declarative programs, including functional programs, only care about the outcome.
Some programming languages, such as Scala, can support both functional programming and object-oriented programming, which is imperative.
In functional programs, the code must obey certain rules. Functions, one of the basic units of code, are at the core of functional programming, and must be written in a way that makes them “pure” functions.
“We’re programming with mathematical functions, which have a lot of constraints,” Lonsdorf said. “They must behave a certain way: they must not have side effects, they’re immutable and they take input and return output.”
Matt Teichman, a lecturer of functional programming in the computer science master’s program at the University of Chicago, said that there are certain ways of writing code in imperative programming that are not done in functional programming.
“A lot of standard situations where you’re used to, for example, creating a global variable and mutating it over time — like maybe in a
for loop or a
while loop — you kind of don’t do that in functional programming,” Teichman said. “Instead, you do this equivalent thing using what’s called ‘higher-order functions.’ You can think of it kind of like a computer program that takes one computer program as an input and returns a different computer program as output.”
“One of the key differences I’ve noticed is that when you program in a functional way, you really get much more than the name that’s coming along with your function.”
These constraints, which force functions to be written in a way that doesn’t manipulate state or have unintended consequences, has an effect on the code that cascades up into larger design implications, Lonsdorf said.
“One of the key differences I’ve noticed is that, when you program in a functional way, you really get much more than the name that’s coming along with your function,” Lonsdorf said.
Teichman said one of the benefits of functional programming is that the constraints force developers to code in a way that is naturally aligned with good design patterns.
“Something that’s emphasized in agile development is, you can change stuff easily ... building your software brick by brick, with smaller components that you gradually assemble into bigger and bigger components, but they’re all separate from each other,” Teichman said. “These are the natural design choices the functional paradigm kind of forces on you, if you’re writing in a functional paradigm, but which you might have to discover and figure it out to start doing, if you’re in an imperative paradigm.”
Teichman said functional programs work well for applications where the quality of code is paramount, but that they can take longer for a developer to code.
“The functional paradigm shines when it’s really important that your app actually works,” Teichman said. “So if the cost of a bug is very high — we’re talking about bank balances.... It takes a little bit longer to get an app that runs and that you can show people, because the compiler is really picky. It will only accept things that are technically 100 percent correct.”
Functional Programming Shares Close Ties With Abstract Math
Teichman has a doctoral degree in natural language semantics, a field of study that examines language by formally breaking down its constructs.
“You can think of it as if you had to teach a computer to understand the rules of English,” he said. “That field is all pursued on pen and paper, just using a mathematical formalism.”
His research brought him closer and closer to computer science, and one day a friend introduced him to functional programming.
“It was so close to the work that I was doing in philosophy and linguistics, I practically felt like I wasn’t even doing anything different from writing my dissertation,” Teichman said. “It’s just now I was using the same mathematical formalism to make the computer do things. And all the same theoretical explanatory power that I was learning in research mode, I found you can now bring to bear to make the computer do things in a really clean and transparent way.”
“Once you start programming with functions you realize that math is available to you because that’s what that was built on,” Lonsdorf said. “You start to see some languages that are really taking that to its limit. You can use all the ideas and properties that hold some abstract algebra in your programs, and to great benefit.”
Functional programming principles may be closely tied to mathematical principles, but Teichman said developers new to functional programming don’t need to study the math behind it in order to start coding.
“It’s really interesting if you like that sort of thing,” he said. “But not everybody is a mathematician, you don’t have to be. You can just get into the groove of writing code.”
How Implementing a Functional Program Can Go Wrong
As robust as functional code is when implemented correctly, it can be easy to break the functional paradigm if developers don’t follow functional requirements.
“The moment you start breaking the contract that you’re working within — this kind of pure mathematical world — it starts to fester and fall apart,” Lonsdorf said.
It’s possible to code using a mixed paradigm, using both functional and imperative programming styles in a single application, but developers have to be careful to keep the code separate when doing so.
“All my nice, testable, easy input-output functions live in the model, and I’ll actually strive to put them there,” Lonsdorf said. “Whereas the functions that are doing all the nasty bits, that are changing all the states and writing files and doing all that stuff are kind of pushed to the edge.”
Although one of the benefits of functional programming is writing clean, maintainable code, Lonsdorf said relying too much on functional programming libraries and point-free syntax, which removes variable names, can instead result in code that is difficult to decipher.
“The moment you start breaking the contract that you’re working within — this kind of pure mathematical world — it starts to fester and fall apart.”
“The syntax is precisely the danger area,” Lonsdorf said. “Because you’re writing functions, it makes sense that you want tools for composing them. So you start to use the pipeline operator, all these things that are techniques to create a pipeline of pure functions. But you can still just write pure assumptions by making a function call — you don’t need a whole library to do this.”
Teichman said that using point-free syntax comes down to a matter of taste.
“I just take it on a case-by-case basis,” Teichman said. “They’re equivalent, they mean the same thing.... [Point-free style] can make it easier to read, because it’s just more concise. But there are also some cases in point-free style, where in order to make it point-free you have to go through weird hoops, and it’s actually simpler just to have the variables in there.”
One thing to be aware of, Teichman said, are languages such as Haskell that do lazy evaluation, where in runtime the language only executes if the result of the execution is needed later in the program. Languages that use lazy evaluation are able to run more quickly because they don’t waste time on unneeded executions.
“But, now and again, you do hit performance issues,” Teichman said. “[Sometimes] you’re in a specific situation where the naive way to write the code doesn’t perform very well. In a lazy evaluated language, it can require really advanced skills to fine-tune your code for performance in those unusual circumstances.”
Can Functional Principles Become More Mainstream?
Despite these challenges, functional programming comes with many benefits. Along with its inherent readability and maintainability, Teichman said functional languages also have good performance, despite being high-level programming languages. They result in code that is easy to test, and have qualities that make writing parallel programs easier.
“Shared state is what kills parallelism,” Lonsdorf said. “Programming functionally allows you to take advantage of massive servers and multi-core [processors] and all the stuff that would not need shared states. And it also gives you the tools, algebraically, to reason about sequential and parallel [operations] in a way that isn’t surprising or custom every single time.”
The developers who know and love the functional paradigm, such as Lonsdorf and Teichman, hope that functional programming will become more mainstream, but they acknowledge that it may be unlikely given how niche it still is, and the code discipline it takes to write effective functional programs.
“It is harder to write, initially,” Lonsdorf said. “And it requires that the reader understand a lot of the principles. So what’s happening in the industry is we have people who are very informed and very experienced, trying to make imperative programming — object-oriented or otherwise — feel the same but still adhere to the functional style, trying to bridge that gap so it’s not as hard to read and understand.”
“I think the jury’s still out on whether these languages are going to take off, or whether the big-picture ideas are just going to imprint on mainstream languages,” Teichman said. “Even people who aren’t comfortable programming in a functional paradigm agree that the design regimen functional programming encourages is good practice, whatever language you’re in.”
“They’re taking in bits and pieces of the functional paradigm because it’s so useful.”
He encouraged early career developers to dabble in functional programming, so that they can learn the good programming patterns its design encourages.
“I could not possibly recommend learning functional programming any more highly,” Teichman said. “Although it’s true you’re not likely to start at a Haskell shop for your first tech job — that’s a niche thing — I think it’s very likely that some of the great ideas from a language like Haskell or OCaml are going to be making their way into the work you do, in some way or another.”
Other programming languages are also adopting more functional behaviors, Teichman said.
Lonsdorf believes that developers can benefit from simply thinking of architectural programs from a functional point of view.
“It always works for me, for my mindset, to [ask how to] extract everything I possibly can into a nice pure function,” Lonsdorf said. “No matter what I’m doing, that almost always has the benefit of helping me in my software.”