Blog

Ideas and insights from our team

Functional programming in Python the simplest way


One of the best ways to learn something is by comparing it to things we already know. This blog post has two main goals. The first one is to introduce the basic concepts of functional programming, and for that we will compare with the main rival paradigm, the imperative programming. The second is to show how to use functional programming in Python. For that, we will show some examples, and you may be surprised to see sometimes you use functional constructions without knowing.

The functional paradigm has its basis on the declarative aspect, which is ruled by two main constraints: the use of expressions and the lack of data mutation. The first one means that the code should tell what it aims to do instead of how to do it, and the second means that once a variable assumes a value it will never change on the course of the program execution. This contrasts with the imperative and object oriented paradigms, where we define statements and change variables (objects' state) on the course of the program.

Python is multi-paradigm language: it supports imperative, OO and functional programming paradigms. For the sake of simplicity, we will use the imperative paradigm to make our comparisons. Lets see some practical examples to compare the concepts we just learned (examples in Python 3):

Expressions vs. statements or "what you want" vs. "how to do"

Lets say you want to sum all values in a list and print the result. You could do:

numbers = [1, 2, 3, 4]
total = 0
for number in numbers:
  total += number
print(total)

In the code above we are telling the computer how to do the sum. We create the total variable then iterate over the list. At each lap we increment the total variable with one of the values in the list. And then we print the total. Now lets do the functional way:

numbers = [1, 2, 3, 4]
print(sum(numbers))

This time we said: give me the sum of all numbers in the list. The code is clearer and more compact, or as we say: more declarative. You may say we’re cheating, because the sum function is already implemented. But we can do it also by using the reduce function, a basic one available in all functional languages:

import functools
numbers = [1, 2, 3, 4]
print(functools.reduce(lambda x, y: x + y, numbers))

reduce does basically (((1+2)+3)+4). If you’re confused about how reduce works, Python docs explain it well.

Mutable vs. immutable

Again, lets use the same example of calculating the total sum of values in a list. This time using an imperative style function:

def sum_lst(lst):
  total = 0
  for number in lst:
      total += number
  return total

As we can see, our function has only one variable called total that is updated on every iteration. This is clearly a case of a mutable variable. Now lets try a functional approach:

def sum_lst(lst):
  if not lst:
      return 0
  else:
      return lst[0] + sum_lst(lst[1:]) # values are returned but no variable is changed

This time we are not updating any variables and are using recursion, which is the functional programming way of doing loops.

First-class functions, Higher order functions and Lambda

A programming language is said to have first-class functions when it supports passing functions as parameters, returning them or assigning them to variables. And higher order functions are functions that receive one or more function as arguments and return a function. Python supports first-class functions:

def call_my_name(name):
  print(name)

call_my_name_again = call_my_name
call_my_name('Anderson') # prints Anderson
call_my_name_again('Anderson') # also prints Anderson

And also higher order functions:

def convert_to(to_what, number):
 return to_what(number)

convert_to(float, 20) # convert 20 to float
convert_to(str, 10) # convert 10 to str

Lets see another example: map is very popular higher order function that you've probably heard of before. It traverses an iterable and applies a function to it:

def double(number):
  return number * 2

print(list(map(double, [1, 2, 3, 4])))

Here is an imperative version of what is happening:

numbers = [1, 2, 3, 4]
new_numbers = []
for number in numbers:
  new_numbers.append(number * 2)
print(new_numbers)

Moving on to lambdas. Lambdas are anonymous functions that are usually passed as parameters to other functions. Here is a Python example:

add_one = lambda number: number + 1
print(add_one(10)) # prints 11

Or using map:

print(list(map(lambda number: number + 1, [1, 2, 3, 4])))

Lambdas in Python can have only one line of code and automatically return the computed value, i.e, they are an expression, and so they don’t need the return statement.

List Compressions

Most times we use loops to iterate over lists and execute actions in its elements. List comprehensions are a more declarative way of doing that. Lets see an example. Here is a regular for iteration:

dogs = ["Todd", "Tom", "Bob"]
big_dogs = []
for dog in dogs:
  big_dogs.append("Big {}".format(dog))
print(big_dogs)

Now lets do the same but now using list comprehensions:

dogs = ["Todd", "Tom", "Bob"]
big_dogs = ["Big {}".format(dog) for dog in dogs]
print(big_dogs)

Sum-up

We introduced some functional programming concepts applying them using Python. Hopefully you can now identify functional constructions when you see one and also be able to use them when possible. Python has tons of other functional tools besides the ones showed in this post, start by looking for functools module.

About Anderson Resende

Software developer at Vinta Software.

Comments