Welcome to Functional Programming!
In this chapter, we are going to explore a different way of telling computers what to do. Most of the programming you have done so far is likely Imperative—telling the computer a list of step-by-step instructions. Functional Programming is different! It’s like using a set of mathematical formulas to transform data into what you want.
Don’t worry if this feels a bit like "maths class" at first. Once you see the patterns, you’ll realize it is a very clean and powerful way to write code that has fewer bugs!
1. Functional vs. Imperative Programming
To understand functional programming, we first need to look at what it *isn't*.
• Imperative Programming: You tell the computer how to do something by changing the state of the program (using variables that change value over time). Analogy: A recipe where you change the state of the ingredients (chop the onions, fry the beef).
• Functional Programming: This is Declarative. You tell the computer what you want by applying functions. You don't change the data; you create new data. Analogy: A mathematical formula like \( f(x) = x + 2 \). If \( x \) is 5, the answer is always 7. It doesn't change the 5; it just gives you a 7.
Key Features of Functional Programming
Statelessness: In functional programming, we avoid changing the "state." This means we don't use global variables that change value as the program runs.
Side-Effect Free: A function should only do one thing: take an input and return an output. It shouldn't "reach out" and change anything else, like printing to a screen or updating a database.
Referential Transparency: This is a fancy way of saying that if you call a function with the same input, you will always get the same output. It’s predictable and reliable!
Quick Review Box:
• Imperative: Tells the computer how (step-by-step, changing variables).
• Functional: Tells the computer what (using functions, no side effects).
• Referential Transparency: Same input = Same output, every single time.
2. Functions as First-Class Objects
In languages like Haskell or Python (when used functionally), functions are treated as First-Class Objects. This means you can treat a function just like any other piece of data, such as an integer or a string.
You can:
1. Pass a function as an argument to another function.
2. Return a function as the result of another function.
3. Assign a function to a variable.
Analogy: Think of a function like a "tool." In procedural programming, tools are locked in a shed. In functional programming, you can put a tool inside a box, give it to a friend, or even have one tool build another tool!
Key Takeaway: If you can treat a function like a variable, it is a First-Class Object.
3. Function Application
In functional programming, we use Function Application. This is simply the process of giving a function its arguments so it can produce a result.
We usually write this as \( f \, x \), where \( f \) is the function and \( x \) is the argument.
Example: If we have a function called square, then square 5 would result in 25.
Did you know? In functional languages, we often don't use brackets for function calls! Instead of square(5), we just write square 5.
4. Higher-Order Functions
This is where things get exciting! A Higher-Order Function is a function that takes another function as an argument, or returns a function as its result. You need to know three main ones for your syllabus:
A. Map
Map takes a function and a list. It applies that function to every single item in the list and returns a new list.
Example: Use map with a "Double" function on the list [1, 2, 3] to get [2, 4, 6].
B. Filter
Filter takes a condition (a function that returns True or False) and a list. It looks at every item and only keeps the ones that meet the condition.
Example: Use filter with an "IsEven" function on [1, 2, 3, 4] to get [2, 4].
C. Reduce (or Fold)
Reduce takes a list and "shrinks" it down to a single value by applying a function repeatedly.
Example: Use reduce with an "Addition" function on [1, 2, 3, 4] to get 10 (because 1+2+3+4 = 10).
Memory Aid: The Kitchen Analogy
• Map: Chopping *all* the vegetables in a basket.
• Filter: Picking only the *ripe* tomatoes from the basket.
• Reduce: Throwing all the vegetables into a pot to make *one* soup.
5. Working with Lists
In functional programming, lists are very important. We usually break them down into three parts:
1. Head: The very first element in the list.
2. Tail: A list containing everything else except the head.
3. Empty List: A list with nothing in it, often shown as [ ].
Example: In the list [10, 20, 30, 40]:
• The Head is 10.
• The Tail is [20, 30, 40].
Common Mistake: Students often think the "Tail" is just the last item (40). It isn't! The Tail is the rest of the list. If you take the head of the tail [20, 30, 40], you get 20!
Key Takeaway: Every non-empty list has a head (an item) and a tail (another list).
Summary and Tips
• Functional programming is declarative and avoids side effects.
• First-class objects mean functions can be passed around like data.
• Higher-order functions (Map, Filter, Reduce) make processing lists very easy.
• Lists are made of a head and a tail.
Exam Tip: If a question asks why functional programming is useful for multi-core processors, remember: because there are no side effects and no shared state, different parts of the program can run on different cores at the same time without interfering with each other!