FP In Python: Is It Genuinely Functional?
Tags: discuss, python, programming
Published: 2022-05-27 15:16:53

---
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8zbw7vfxgzx35j3mukl8.jpg
---
 
If you have followed me on Twitter, you may have noticed that I tweeted something about FP and OOP being the same in Python. I later realized that most Pythonistas didn't get the idea I was trying to deliver. Therefore, I decided to write an article to give you a whole rundown of my recent discoveries in Python.

**Edit**: This post was published months ago, I was editing it, and forget to publish the edits. For some reason, I removed it from publication, not sure why. Therefore, this is just a re-upload of an already published post.

## 👉 Table Of Contents (TOC). <a name="TOC"></a>

* [Introduction.](#introduction)
* [What is Object-Oriented Programming?](#what-is-oop)
* [What is Functional Programming?](#what-is-fp)
* [Conclusion.](#conclusion)
* [References & Resources.](#references-resources)

## Introdcution. <a name="introduction"></a>

##### 🔝 [Go To TOC](#TOC). 

Object-oriented programming and functional programming are not the same thing, and they are two different paradigms with their own advantages and disadvantages. However, the division between the two paradigms has been blurred in python since everything is modeled as an object stored in a heap \[[0\]](#0). In fact, it turned out that everything you can do with classes, such as inheritance, and composition, can also be achieved with functions in python: aka callable objects.

To give you an idea of what I'm referring to, let's go over an overview of the two paradigms.

## What is Object-Oriented Programming? <a name="what-is-oop"></a>

##### 🔝 [Go To TOC](#TOC). 

Object-oriented programming is a programming paradigm that represents the concept of **objects** as data structures that contain data, aka **states**, and methods, aka **behaviors**. It is a way of designing programs by breaking them down into smaller parts called **objects**, which can be used to model real-world entities like animals, cars, etc. The object-oriented approach to programming has been around since the 1960s, and it has been popularized by languages such as Java, C++, Python, Ruby, etc.

Since this article is tied only to the python language, let's take the following listing to model an animal with a behavior. For simplicity reasons, let's assume that an animal has only two primary properties: a name and a color. Additionally, we can use the `attrs` package to avoid boilerplate code(e.g., `__init__`) in our class.

```python
from attrs import define

@define
class Animal:
  name: str
  color: str
  def run(self) -> None:
    print(f"An animal whose name: {self.name} and color: {self.color} is currently running!")
```

In python 2, the definition of that class would be slightly different: `class Animal(object).` Since we are using python 3, every class inherits from the `object` class by default. Moreover, objects can be created, manipulated, and destroyed. The object's data can be accessed using the object's name, a reference variable, followed by the dot operator, called "dot notation.". The same applies to the object's methods. 

```python
animal = Animal("An animal", "purple")
print(animal.name)
animal.run()
animal.color = "blue"
del animal
```

Running the previous snippet of code will result in the following being displayed on your terminal:

```sh
An animal
An animal whose name: An animal and color: purple is currently running!
```

This programming paradigm uses inheritance to create inheritance hierarchies. An object can be viewed as a template for the data and behavior of an object. Object-oriented programming is practical when dealing with complex data structures.

Now let's model a specific type of animal: A Dog which, as you may know, can be achieved with the power of inheritance.

```python
from attrs import define

@define
class Dog(Animal):
  def run(self) -> None:
    print(f"A dog whose name: {self.name} and color: {self.color} is currently running!")
```

As noted earlier, an object can be created, manipulated, and destroyed. States and behaviors of an object can be accessed using dot notation.

```python
dog = Dog("Finn", "white")
dog.run()
dog.color = "blue"
del dog
```

Running the previous listing will result in the following being displayed on your terminal:

```sh
A dog whose name: Finn and color: white is currently running!
```

You can even do multilevel inheritance.

```python
from attrs import define

@define
class Puppy(Dog):
  def run(self) -> None:
    print(f"A puppy whose name: {self.name} and color: {self.color} is currently running!")
```

In addition to that, you can do composition; you get the point.

```python
from attrs import define

@define
class Puppy(Animal, object):
  def run(self) -> None:
    print(f"A puppy whose name: {self.name} and color: {self.color} is currently running!")
```

Now, let's move on to the functional programming paradigm to see if we can achieve the same logic with functions.

## What is Functional Programming? <a name="what-is-fp"></a>

##### 🔝 [Go To TOC](#TOC). 

Functional programming is a programming paradigm that treats computation to evaluate mathematical functions and avoids side-effects and mutable data. It means that this paradigm does not use variables to store information. Instead, it uses immutable values, which cannot be changed after they are created. It is a declarative paradigm, which means that the program's flowchart consists of statements such as `f(x) = a.x + b`.

The main difference between functional and object-oriented programming is that functional programs are more concerned with mathematical functions, and other types of computations. In contrast, object-oriented programs are more concerned with objects that have states and behaviors.

Now let's explore this programming paradigm by transforming our previous examples into functions.

```python
def animal(name: str, color: str) -> None:
  def run():
  	print(f"An animal whose name: {name} and color: {color} is currently running!")
  return run
```

Until now, everything looks as usual. But our `animal` callable object is missing attributes which begs the question: Is it doable to do so in python? And the answer is yes, ever since pep-232 \[[1\]](#1).

```python
def run() -> None:
  global animal
  print(f"An animal whose name: {animal.name} and color: {animal.color} is currently running!")

def animal():
  ...
```

Now, we can define attributes and methods for the `animal` callable object as follows:

```python
animal.name = "An animal"
animal.color = "blue"
animal.run = run
```

Now, we can call the inner callable object as shown in the listing below:

```python
animal.run()
```

Running the previous snippets of code will result in the following being displayed on your terminal:

```sh
An animal whose name: An animal and color: blue is currently running!
```

We can even modify the inner attributes as shown below:

```python
animal.name = "Another animal"
animal.run()
del animal
```

Which results in:

```sh
An animal whose name: Another animal and color: blue is currently running!
```

With that noted, you start to see a considerable similarity between both animal objects created using a class or function(not a function, just a callable object). Now, this begs the question: is it possible to do inheritance using functions. And the answer is yes, with the help of python decorators.

```python
def run() -> None:
  global animal
  print(f"An animal whose name: {animal.name} and color: {animal.color} is currently running!")

def animal(func):
  def inner(*args, **kwargs):
    return func(*args, **kwargs)
  return inner

animal.name = "An animal"
animal.color = "blue"
animal.run = run

@animal
def dog():
  ...
```

Now, we can define attributes and methods for the `dog` callable object as follows:

```python
def run():
  global dog
  print(f"A dog whose name: {dog.name} and color: {dog.color} is currently running!")

dog.name = "Finn"
dog.color = "blue"
dog.run = run
```

Now, we can call the inner callable object as shown in the listing below:

```python
dog.run()
```

Output:

```sh
A dog whose name: Finn and color: blue is currently running!
```

We can even modify the inner attributes as shown below:

```python
dog.name = "Katie"
dog.run()
del dog
```

Output:

```sh
A dog whose name: Katie and color: blue is currently running!
```

However, the effect of the decorator is entirely demolished because we didn't do much in the inner function. To illustrate the mechanism of the inner function, we can call the run function of the callable animal object.

```python
def run() -> None:
  global animal
  print(f"An animal whose name: {animal.name} and color: {animal.color} is currently running!")

def animal(func):
  def inner(*args, **kwargs):
    animal.run()
    return func(*args, **kwargs)
  return inner

animal.name = "An animal"
animal.color = "blue"
animal.run = run

@animal
def dog() -> None:
  ...
```

Now, instantiating the object dog will execute the run function of the callable animal object:

```python
dog()
```

Output:

```sh
An animal whose name: An animal and color: blue is currently running!
```

You can even do multilevel inheritance like the following:

```python
def run() -> None:
  global animal
  print(f"An animal whose name: {animal.name} and color: {animal.color} is currently running!")

def animal(func):
  def inner(*args, **kwargs):
    animal.run()
    return func(*args, **kwargs)
  return inner

animal.name = "An animal"
animal.color = "blue"
animal.run = run

def run():
  global dog
  print(f"A dog whose name: {dog.name} and color: {dog.color} is currently running!")

@animal
def dog(func):
  def inner(*args, **kwargs):
    dog.run()
    return func(*args, **kwargs)
  return inner

dog.name = "Finn"
dog.color = "blue"
dog.run = run

@dog
def puppy() -> None:
  ...

puppy()
```

Output:

```sh
An animal whose name: An animal and color: blue is currently running!
A dog whose name: Finn and color: blue is currently running!
```

At this point, it seems like I lost my damn mind. You know what, screw it. You can even do composition with decorators by chaining them together:

```python
def run() -> None:
  global animal
  print(f"An animal whose name: {animal.name} and color: {animal.color} is currently running!")

def animal(func):
  def inner(*args, **kwargs):
    animal.run()
    return func(*args, **kwargs)
  return inner

animal.name = "An animal"
animal.color = "blue"
animal.run = run

def run() -> None:
  global dog
  print(f"A dog whose name: {dog.name} and color: {dog.color} is currently running!")

def dog(func):
  def inner(*args, **kwargs):
    dog.run()
    return func(*args, **kwargs)
  return inner

dog.name = "Finn"
dog.color = "blue"
dog.run = run

@animal
@dog
def puppy() -> None:
  ...

puppy()
```

Running the previous snippet of code will result in the following output:

```python
An animal whose name: An animal and color: blue is currently running!
A dog whose name: Finn and color: blue is currently running!
```

Holy smokes! What even is this? Am I just tripping, or the whole language is a complete mess? This results in me questioning my existence.

That was a bit dramatic. It is just the language giving you a lot of flexibility to invent whatever the hell you can think of. But, there is a significant trade-off here because it would make pure functional programming impossible to do in python. It would also result in huge memory consumption by making everything objects. Python is a weird programming language.

## Conclusion. <a name="conclusion"></a>

##### 🔝 [Go To TOC](#TOC). 

<figure>
  <img src="https://memegenerator.net/img/instances/66075049/it-is-inevitable.jpg">
  <figcaption>[memegenerator.net](https://memegenerator.net/instance/66075049/mr-smith-it-is-inevitable).</figcaption>
</figure>


Objects in python are inevitable. You may think that you are doing pure functional programming, but it turns out it is not the case. You are just using callable objects to build your logic which takes space on the heap, which is not the usual approach unlike other programming languages that uses a stack to allocate frames for functions.

I am not sure why Guido thought making everything inherit from the object class was a good idea cause, as you may know, it will consume loads of ram to run a relatively complex python program.

In conclusion, purely functional programming is entirely unfeasible to do in python. Bear in mind that the purpose of this article is not to bash the language itself but rather to point out what I think are fundamental flaws in the design of the language; not sure if this issue can be fixed because the language has evolved drastically over the years.

It is also quite important to mention that, in theory, building relatively complex web frameworks based on python will result in high memory consumption, again due to the fact that everything in python is just an object. 

Key takeaway:

* There is no such thing as a function in python. Instead, You can only use objects to create a callable object which is not a pure function. 

There is a lot to discuss on this topic regarding performance, scalability, maintainability, etc. Presumably, I will investigate it even further whenever I have the time to do so. In the meantime, I am looking forward to your responses. What do you think? Is there any significant difference between the two paradigms in python?

That's all for today's article. Thank you for reading! Stay hydrated, folks; see you in the next one.

<em>Image by <a href="https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1513945">Gerd Altmann</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1513945">Pixabay</a></em> 

## References & Resources. <a name="references-resources"></a>

##### 🔝 [Go To TOC](#TOC). 

[0] <a name="0"></a> docs.python.org, python 3.10.4. [Memory Management](https://docs.python.org/3/c-api/memory.html).

[1] <a name="1"></a> Barry Warsaw, 2000. [PEP 232 – Function Attributes](https://peps.python.org/pep-0232/).

[2] <a name="2"></a> Wikipedia, the free encyclopedia. [Object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming).

[3] <a name="3"></a> Wikipedia, the free encyclopedia. [Functional programming](https://en.wikipedia.org/wiki/Functional_programming).