Skip to content
Happy Programming Guide
Start learning
Programming Basics

Functional Programming vs OOP

Objects bundle state with the code that changes it; functional programming keeps data and transformations apart and avoids changing anything. The same problem both ways, and when each wins.

A pen and an open notebook ready for notes

Object-oriented programming organises a program around things that hold state and expose operations on it. Functional programming organises it around transformations of data that never change their inputs. Most modern languages support both, most real programs mix them, and the useful question is not which paradigm is better but which one fits the part of the program you are writing.

The two ideas#

OOP: group data with the methods that operate on it into objects, hide the internal details, and let objects change over time through those methods.

FP: write functions that take inputs and return outputs without changing anything else, keep data immutable, and build programs by composing those functions.

Neither is about syntax. A language with classes can be written functionally; a language with first-class functions can be written in an object style. It is about where state lives and who is allowed to change it.

The same problem, both ways#

A shopping basket: add items, apply a discount, get the total.

Python
# Object-oriented: the basket holds state and changes it
class Basket:
    def __init__(self):
        self.items = []
        self.discount = 0.0

    def add(self, name, price):
        self.items.append((name, price))

    def apply_discount(self, fraction):
        self.discount = fraction

    def total(self):
        subtotal = sum(price for _, price in self.items)
        return subtotal * (1 - self.discount)


basket = Basket()
basket.add("book", 12.0)
basket.add("pen", 3.0)
basket.apply_discount(0.1)
print(basket.total())          # 13.5
Python
# Functional: data is plain, functions return new data
def add(items, name, price):
    return items + [(name, price)]           # a new list; the old one is untouched

def total(items, discount=0.0):
    subtotal = sum(price for _, price in items)
    return subtotal * (1 - discount)


items = []
items = add(items, "book", 12.0)
items = add(items, "pen", 3.0)
print(total(items, discount=0.1))          # 13.5

Both work. The OOP version has one thing, the basket, that you talk to and that remembers. The FP version has data that flows through functions, and at any moment you can hold the old version and the new version side by side.

What immutability buys#

In the functional version, add cannot surprise anyone: given the same inputs it returns the same output and touches nothing else. That property, being pure, has consequences:

  • Testing is trivial. Call the function, check the return value. No setup, no teardown, no “what state was the object in”.
  • Concurrency is safe by default. Two threads cannot corrupt data that nothing changes.
  • Reasoning is local. To understand a call, read the function. You never need to know what happened earlier.
  • Undo and history are free. Keep the old value; it is still valid.

The cost is that “change” means building a new value, which is more work to write and sometimes more work for the machine, though languages designed for it make both cheap.

What objects buy#

  • A natural model for things with identity. A user account, a network connection, a game character: they exist over time and change. An object matches that directly.
  • Encapsulation. The rest of the program cannot put the object into an invalid state, because only its methods can touch the internals.
  • Polymorphism. Different kinds of thing responding to the same message — shape.area() for a circle or a square — without the caller caring which.
  • Familiarity. Most frameworks, libraries and teams are organised this way.

The cost is that state spread across many mutable objects becomes hard to reason about, and the bugs that result — something changed it, but what? — are the expensive kind.

Where each fits#

Part of the program Leans Why
Data transformation, pipelines, calculations FP Inputs to outputs, nothing to remember
Long-lived things: sessions, connections, UI widgets OOP Identity and lifecycle matter
Business rules FP Pure rules are testable and composable
Frameworks and plugins OOP Interfaces and polymorphism define extension points
Concurrency FP Immutable data needs no locks
Simulation and games Mixed Entities are objects; update rules are often functions

How they combine#

The pattern most experienced developers settle on: objects at the edges, functions in the middle. Objects manage the things that genuinely have state — connections, the application itself, the UI — and they call pure functions to do the actual work. Data is passed as plain immutable values; a class is used when something needs identity or an interface.

Python
# A class with state, delegating the logic to pure functions
class OrderService:
    def __init__(self, repository):
        self.repository = repository            # a connection: legitimately stateful

    def checkout(self, basket_items, discount):
        amount = total(basket_items, discount)  # pure function: easy to test alone
        order = build_order(basket_items, amount)
        self.repository.save(order)             # the one side effect, at the edge
        return order

Python, JavaScript, C#, Java, Kotlin and Swift all support this mix comfortably. Languages such as Haskell and Elixir push you towards the functional end; Java and C# historically pushed towards objects but have absorbed a great deal of FP since.

Questions people ask#

Is one paradigm faster?

Not inherently. Immutable data can cost allocations; mutable shared state can cost locks. Performance depends on the language and the workload far more than the paradigm.

Should a beginner learn OOP or FP first?

Learn functions first, because everything uses them; then classes, because most code you will read uses them. The paradigm labels can wait until both feel natural.

Is FP only for maths-heavy code?

No. Web request handling, data cleaning, configuration and business rules are all naturally functional: input in, output out.

What are closures and higher-order functions?

Functions that take or return other functions, and functions that remember the variables around them. They are the FP tools for composition, and both exist in every language mentioned here.

Where to go next#

Functions and functional programming, from first principlesRead 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 *