Skip to content
Happy Programming Guide
Start learning
Programming Basics

Functions and Functional Programming

What functions really are, what makes one pure, and the functional ideas — map, filter, reduce, immutability — that make everyday code easier to test.

A pen and an open notebook ready for notes

A function is a named piece of work you can run more than once. Functional programming is a set of habits about how you write them: return values instead of changing things, avoid hidden state, and build bigger operations out of small ones. You do not need to adopt a functional language to get most of the benefit — the ideas apply directly to Python, JavaScript, Java and C#.

The basics, briefly#

Python
def area(width, height):
    return width * height


print(area(3, 4))            # 12
print(area(height=4, width=3))  # same, named arguments

Three parts: the name, the parameters it accepts, and what it gives back. A function that gives nothing back returns None in Python, undefined in JavaScript, and is declared void in Java and C#.

Variables created inside a function are local to it:

Python
def calculate():
    total = 10      # only exists inside this function
    return total


calculate()
print(total)        # NameError - total is not defined out here

That isolation is the point. It means you can read a function and understand it without reading the rest of the program.

Pure functions#

A function is pure when two things are true: the same inputs always give the same output, and it changes nothing outside itself.

Python
# Pure
def add_tax(price, rate):
    return price * (1 + rate)


# Not pure - reads something outside itself
TAX_RATE = 0.2

def add_tax(price):
    return price * (1 + TAX_RATE)


# Not pure - changes something outside itself
totals = []

def record(price):
    totals.append(price)

Pure functions are easier to work with for concrete reasons, not aesthetic ones:

  • You can test them in one line. No setup, no database, no mocking — call it and check what comes back.
  • You can read them in isolation. Nothing elsewhere in the program can change what they do.
  • You can cache them. If the inputs match a previous call, the previous answer is still correct.
  • You can run them in parallel without worrying about two of them clashing.

Not everything can be pure — something has to write files, print output and talk to databases. The useful goal is to push those effects to the edges and keep the decision-making in the middle pure.

map, filter and reduce#

These three cover most of what loops do to collections.

Python
numbers = [1, 2, 3, 4, 5, 6]

# map - transform every item
doubled = [n * 2 for n in numbers]

# filter - keep some items
evens = [n for n in numbers if n % 2 == 0]

# reduce - collapse to a single value
total = sum(numbers)

Python prefers comprehensions to the map and filter functions, but the concepts are identical. In JavaScript the methods are used directly:

JavaScript
const numbers = [1, 2, 3, 4, 5, 6];

const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const total = numbers.reduce((sum, n) => sum + n, 0);

The gain is that the name says what is happening. A for loop could be doing anything; filter can only be removing items.

They chain, which is where the style earns its keep:

JavaScript
const total = orders
  .filter(o => o.status === "paid")
  .map(o => o.amount)
  .reduce((sum, amount) => sum + amount, 0);

Immutability#

Changing data in place causes bugs that are hard to trace, because the change is visible everywhere the data is referenced:

Python
def add_bonus(scores):
    scores.append(10)     # modifies the caller's list
    return scores


original = [1, 2, 3]
result = add_bonus(original)
print(original)   # [1, 2, 3, 10] - the caller's list changed too

Return a new value instead:

Python
def add_bonus(scores):
    return scores + [10]     # a new list


original = [1, 2, 3]
result = add_bonus(original)
print(original)   # [1, 2, 3]  - untouched
print(result)     # [1, 2, 3, 10]

In JavaScript the spread operator does the same job:

JavaScript
const withBonus = [...scores, 10];
const updated = { ...user, name: "Ana" };

Functions as values#

In most modern languages a function is a value you can store, pass and return.

Python
def shout(text):
    return text.upper()


action = shout              # no brackets - the function itself
print(action("hello"))      # HELLO

names = ["ana", "sam"]
print([action(n) for n in names])   # ['ANA', 'SAM']

Passing a function as an argument is how sorting keys work:

Python
people = [{"name": "Ana", "age": 34}, {"name": "Sam", "age": 28}]

by_age = sorted(people, key=lambda p: p["age"])

Returning a function lets you build one that remembers something:

Python
def multiplier(factor):
    def multiply(n):
        return n * factor      # remembers factor
    return multiply


double = multiplier(2)
triple = multiplier(3)
print(double(10), triple(10))   # 20 30

That inner function keeping hold of factor after the outer one has finished is called a closure. It is the same mechanism behind event handlers and decorators.

Composition#

Small functions combine into bigger ones without either knowing about the other:

Python
def strip_spaces(text):
    return text.strip()


def lowercase(text):
    return text.lower()


def clean(text):
    return lowercase(strip_spaces(text))


print(clean("  Hello World  "))   # hello world

Each piece is a few lines, testable on its own, and reusable somewhere else. That is the practical payoff of the whole style: not elegance, but that you can change one part without reading the others.

Questions people ask#

Do I have to learn a functional language?

No. Haskell and Elixir push the ideas further, and they are interesting, but the useful parts — pure functions, avoiding shared mutable state, map and filter — all work in the languages you already use.

Is functional code slower?

Creating new lists rather than modifying in place costs some memory and time. In almost all application code the difference is irrelevant next to the reduction in bugs. In a hot inner loop, measure before deciding.

What is a lambda?

A short function with no name, written inline. Useful as a sorting key or a one-line callback. If it needs a comment to explain, give it a real name with def instead.

Is object-oriented programming the opposite of this?

No, and treating them as rivals is a false choice. Most good codebases use objects for structure and functional habits inside methods. The two ideas answer different questions.

Where to go next#

Object-oriented programming, the other half of the pictureRead next

Keep reading

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *