Introduction to Functional Programming

By Martin McBride, 2018-03-15

Categories: functional programming pure function lambda function iterator higher order function iterator


Functional programming is one of several programming paradigms that Python supports.

What is functional programming

In functional programming, functions are first class objects, and we use them as the basic building blocks of our programs (just as you might use objects to build your program if you are using object oriented programming).

Of course, in plain old procedural programming, we use functions to structure our code. The difference with functional programming if that function don't just operate on data, functions also operate on other functions. That is what we mean by functions being first class objects - you can store a function object in a variable, pass a function into a function, even return a function from a function, exactly as you could with a number, string or list.

Functional and object oriented programming both aim to simplify complex programs by using abstractions. OOP works well in scenarios where the software is structurally complex because it organises the program's data into self contained units that interact in controlled ways. Functional programming works well with software that is algorithmically complex - for example "artificial intelligence" type applications. Python allows you to mix both styles.

Characteristics of functional programming

Just like OOP, there are no exact rules that you have to follow to create functional code. But there are certain characteristics and patterns functional programming tends to follow:

  • Functions are first class objects
  • Functions with side effects are avoided
  • Iterators tend to be used instead of lists and other data structures
  • Higher order functions are often used
  • Loops and if statements tend to be used less often - higher order functions and recursion are used instead

You will also see lambda functions, closures, partials and currying being used. We will look at some of those aspects in the rest of this article.

Avoiding functions with side effects

Quite often when you call a function it will have some lasting effect on the system - a side effect. Examples are:

  • File functions might write, modify or delete files on the disk
  • The print function displays something on the console
  • A function might alter the objects that are passed into them, eg adding an extra value to a list that has been passed as a parameter
  • A function might update a global variable

We often need side effects (programs sometimes have to write data to disk, for example), but they can make the behaviour of a program quite unpredictable. In functional programming we try to limit side effects by using pure functions (functions with no side effects) wherever possible. In particular, if a system involves complex algorithms, we might try to implement these in a subsystem that uses the functional programming paradigm. Elements such as disk i/o or user interfaces can then be implemented separately.

Pure functions and provability

Here is an example of a pure function:

def add(a, b):
    return a + b

Calling this function has an entirely predictable effect. It return the sum of a and b, and has absolutely no other side effect.

If we build our code entirely out of pure functions, the entire behaviour is predictable.

In theory we could even mathematically prove that the function works correctly. In practice, any useful program is usually too complex to be proved correct in this way, but functional code is still easier to test because there are no external factors to worry about.

Iterators

Procedural programs often work directly on lists, arrays and other data structures. A potential problem with that is that your program might change the data, deliberately or even accidentally. Changing the data causes a side effect, which we are trying to avoid.

One way to avoid this is to use iterators. You can obtain an iterator from a list (or any iterable item), using the iter function. Once you have an iterator, you can read the elements of the list one by one, using the next function:

k = [10, 20, 30, 40]

it = iter(k)

print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
print(next(it)) # 40

The advantage of this is that an iterator only allows you to read elements. If you pass an iterator into a function, rather than the list itself, the function can't change the list.

Another advantage of iterators is that they are lazy - they will only request the next element when they need it. This can be very useful when processing long sequences.

Higher order functions

Higher order functions are functions that act on other functions. That doesn't mean functions that call other functions, it means functional that accept functions as parameters, or have a function as their return value.

As this is quite key to the idea of functional programming, as you might imagine there are quite a few built-in higher order functions, and it is also easy to write your own.

As a simple example, the map function accepts a function and a sequence of values as parameters. It applies the function to each item in the sequence, and returns the result as an iterator. For example:

def add3(x):
    return x+3

it = map(add3, [1, 2, 3, 4])

print(list(it)) # [4, 5, 6, 7]

We have defined a simple function that adds 3 to its input. We then use map to apply that function to the list [1, 2, 3, 4] giving the result [4, 5, 6, 7].

Now, clearly, we could have done the same thing with a simple loop. Why bother with map? Well, it hides the unimportant implementation detail (the loop) and lets you concentrate on what really matters (the function is being applied to the list of values). This means that if you need to do more complicated things, maybe with several higher order functions, you don't get bogged down in the details.

Another advantage is that it automatically supports lazy iteration. When you call the map function, it doesn't actually do any calculations. It simply returns a special iterator (a map object) that knows what to do when it is asked to. No matter how long the list, map will return very quickly.

As you can see from the code, we can't just print it (the result of the call to map) - well we could, but the result would be pretty boring, it is just a map object. To see the actual values we must convert it into a list. What the list function does is to continuously call next on the map object, and store each value in the list. This is when the actual calculations are done.

See also

If you found this article useful, you might be interested in the book NumPy Recipes or other books by the same author.

Join the PythonInformer Newsletter

Sign up using this form to receive an email when new content is added:

Popular tags

2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function latex len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template tex text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest