Based on my interests, functional programming should have been a very fun course for me. Our teacher was very passionate. We had to write code. But something went wrongâI didn't click with what the teacher was telling us. Why were lists so interesting? Why was the syntax so backward and full of parentheses? Why would I use these things when it was much simpler to write the same code in C++? I ended up trying to translate all the programming constructs I knew from BASIC and C++ into Lisp and OCaml. It completely missed the point of functional programming, but I passed the course and forgot about it for many years.
I imagine that many of you can relate to this story, and I have a possible reason for this. I now believe that my teacher, despite being extremely passionate, used the wrong approach. Today, I understand that functional programming has a certain elegance at its core, due to its strong relationship with mathematics. But that elegance requires a sense of insightful observation that I didn't have when I was 20, that is, a sense that I was lucky to build on after years of various experiences. It's obvious to me now that learning functional programming shouldn't be related to the ability of the reader to see this elegance.
So, what approach could we use instead? Thinking about the past me, that is, the geek who just wanted to write code, there's only one way to goâlook at the common problems in code and explore how functional programming reduces or removes them entirely. Additionally, start from the beginning; you've already seen functional programming, you've already used some of the concepts and constructs, and you might have even found them very useful. Let's examine why.
Around 10 years after I finished the university functional programming course, I had a casual chat with my friend, Felix. As any two geeks, we would rarely see each other, but we had, for years, an ongoing conversation on instant messaging discussing all kinds of nerdy topics and, of course, programming.
Somehow, the topic of functional programming came up. Felix pointed out that one of my favorite and most enjoyable programming languages, LOGO, was, in fact, a functional programming language.
LOGO is an educational programming language whose main characteristic is utilization of so-called turtle graphics.
It was obvious in retrospect; here is how to write a function that draws a square in the KTurtle version of LOGO:
learn square {
repeat 4 {forward 50 turnright 90}
} The result is shown in the following screenshot:
Can you see how we're passing two lines of code to the repeat function? That's functional programming! A fundamental tenet of functional programming is that code is just another type of data, which can be packed in a function and passed around to other functions. I used this construct in LOGO hundreds of times without making the connection.
This realization made me think: could there be other functional programming constructs that I've used without knowing? As it turns out, yes, there were. In fact, as a C++ programmer, you've most likely used them as well; let's take a look at a few examples:
int add(const int base, const int exponent){
return pow(base, exponent);
} This function is a typical example of recommended C++ code. I first learned about the benefits of adding const everywhere from the amazing books of Bertrand Meyer: Effective C++, More Effective C++, and Effective STL. There are multiple reasons this construct works well. First, it protects the data members and parameters that shouldn't change. Second, it allows a programmer to reason more easily about what happens in the function by removing possible side effects. Third, it allows the compiler to optimize the function.
As it turns out, this is also an example of immutability in action. As we'll discover in the following chapters, functional programming places immutability at the core of the programs, moving all side effects to the edges of the program. We already know the basic construct of functional programming; to say that we use functional programming just means to use it much more extensively!
Here's another example from STL:
std::vector aCollection{5, 4, 3, 2, 1};
sort (aCollection.begin(), aCollection.end()); The STL algorithms have great power; this power comes from polymorphism. I'm using this term in a more fundamental sense than in OOPâit merely means that it doesn't matter what the collection contains, because the algorithm will still work fine as long as a comparison is implemented. I have to admit that when I first understood it, I was impressed by the smart, effective solution.
There's a variant of the sort function that allows the sorting of elements even when the comparison is not implemented, or when it doesn't work as we'd like; for example, when we are given a Name structure, as follows:
using namespace std;
// Parts of code omitted for clarity
struct Name{
string firstName;
string lastName;
};
If we'd like to sort a vector<Name> container by first name, we just need a compare function:
bool compareByFirstName(const Name& first, const Name& second){
return first.firstName < second.firstName;
} Additionally, we need to pass it to the sort function, as shown in the following code:
int main(){
vector<Name> names = {Name("John", "Smith"), Name("Alex",
"Bolboaca")};
sort(names.begin(), names.end(), compareByFirstName);
}
// The names vector now contains "Alex Bolboaca", "John Smith" This makes a kind of higher-order function. A high-level function is a function that uses other functions as parameters in order to allow higher levels of polymorphism. Congratulationsâyou've just used a second functional programming construct!
I will go as far as to state that STL is a good example of functional programming in action. Once you learn more about functional programming constructs, you'll...