OOP and Functional Programming (for the sake of my sanity, only talk about Python)

Hey y’all, so I am trying to finish this project. Technically this is my unfinished Devember project, but I never figured that I would be able to finish it as it’s my first real-world project - well, sort of. Anyway, I am not a very experienced programmer, particularly with OOP and its paradigms. I even constantly have to reiterate to myself the differences between objects, classes, instances, inheritance, constructors, destructors, composition, etc, etc. It’s all too much tbh. At the same time, like I’ve never struggled with a programming concept so much ever before. Rather than making me feel bad, this has made me feel rather sour about using OOP techniques in my own programming. However, don’t take me the wrong way. I realize that OOP is a tool; and it is necessary, so I am trying harder to understand this concept than I’ve ever tried on any other concept. It’s just that OOP is a proverbial hammer, but not everything is a nail. That being said, colleges seem to not realize this. Something that I see over and over again is about Functional Programming Paradigms and Procedural Programming. I’ve even seen a lot of YouTube videos talking about some of the bad things of using OOP all the time - hence my opinion above.

To my understanding, Procedural Programming is what they teach you when you first start out with programming, like for example, the programming on my TI-84 Plus CE - TI Basic. And I’ll also attach one of my own programs (written in C++) that I think is procedural. The problem is that college doesn’t even mention Functional Programming Paradigms. I have to use the internet for that (heck, I have to use the internet for everything they do [pretend to] teach anyway). Thus, I want to understand Fucntional programming. But tbh, I also think it may be helpful for me to understand functional programming if I also understand OOP better.

Conclusion and TL;DR: Can I get some insight into OOP and Functional Programming paradigms, their differences, their strengths, and their weaknesses? I am looking for facts as much as I am opinions. I’d like to gather a bunch of different insights into such complex topics - as I know many programming concepts are (such as using whitespace as syntax lol).

2 Likes

Memory.zip (2.6 KB)

Oooh, this should be fun :smiley:

OOP: Pretty much outdated and legacy these days; the concept of inheritance as a solution to polymorphism is at this point pretty much acknowledged as a very inefficient way to solve that particular problem, and better ones have been invented (such as Templates). That’s not to say OOP is all bad; but the core is pretty weak, and require very careful coding and discipline to get right. Even more so than procedural and functional programming.

This C++ talk is great about touching the major pain points and what to watch out for, but I recommend you look at it when you have worked on a project with at least 20 classes and an actual class hierarchy. Back to Basics: Object-Oriented Programming - Jon Kalb - CppCon 2019 - YouTube

In Python, it’s more common with an object based paradigm than an object oriented paradigm in my experience. That is, there is very little inheritance and polymorphism; most of it is instead objects within objects.

If you really want to learn functional programming best way is to start learning a functional language like Lisp or Haskell. If you want something more popular then JavaScript is the way to go. :slight_smile:

3 Likes

Yeah, I’ve heard that composition is more favorable than inheritance. I’ve also heard that OOP is bad because of state. But what is state? Personally, I can see the blatantly obvious issue of OOP being that it encourages complexity where there is no need for any with a lot of programmers. For example, would you write “Hello World!” by defining a class first? Well a lot of programmers seem to be writing code functionally equivalent to doing that, and even a nube like myself can see that.

I would like to challenge you with a question? What defines an OOP language? Does not Python have Functional features? Kotlin advertises FP compatibility.

"How do you look at a language and decide if it is a functional programming language or not? … I’ll give a couple of examples…
‘So it’s a functional programming language if it has support for first class functions.’
‘Oh, ok, so Java is a functional programming language?’
‘No, no, not Java. They have to be pure.’
‘Oh ok so Camel’s not a functional programming language. Clojure’s not a functional programming language?’
‘No no, those count, but ah, ok you need immutability. That’s important.’
‘Oh okay so Rust is a functional programming language? It has immutability by default even.’
One person even suggested a score so like if you have a certain number of things checked off and you cross a threshold, then it’s functional. But ultimately, I ended up concluding that, to be perfectly honest, that classifying languages as functional or not is kinda arbitrary, definitely very fuzzy; and ultimately not as important as the style and their support for the style. [Such as] being able to avoid mutation and side-effects and still have a really good experience. So to answer your question directly. Like would I say, ‘Lisp is, like, a good Functional Programming language?’; it really depends on the Lisp. Like if there’s a Lisp where, like, every part of the standard library requires you to mutate things and do side-effects, maybe not. Clojure definitely is, so I really just think it depends on the language. "

This was an excerpt of a 45min talk given by Richard Feldman on YT about “Why isn’t Functional Programming the Norm?” and I watched the whole thing. Actually this particular video is what inspired me to create this forum thread. Essentially, unless you guys have a better argument than this guy, I’m really gonna take your suggestions for languages that I should learn with a grain of salt.

1 Like

Right now, I am focusing on two languages in particular to get good at them: C and Python. C transcends both OOP and Functional. And I know for a fact that it is possible to do functional in Python. The only problem is that I don’t particularly understand what functional is. Heck, I can barely even wrap my brain around OOP.

C does not have support for “true” OOP; you can write OOP in it, but saying that is the same as saying you can run a 25 mile marathon in high heels. Clearly not what you want to use for such a task. When we say “this language is OOP” or “this language is Functional” what we mean is that this or that language is especially suited for this or that paradigm.

From my perspective there are four languages to learn for beginners, perhaps not simultaneously, but as a starting point:

  • Python, to teach you the basics as well as make your code readable by enforcing proper indentation;
  • C#, for the OOP
  • C, for memory pointers and low-level shenanigans like bit shuffling and why UTF-8 is the way to go
  • JavaScript, for asynchronous calls, functional programming and lambdas

These four complement each other remarkably well and build a great base and understanding for which tools to use for what task.

Technically speaking we’ve all done “functional programming” for ages; but I’d say the important two parts are:

  • Functions must be passed around and treated as objects
  • Functions will always do one thing, will always return the same answer (except for randomizer functions) with the same input, and will never leave any side effects.

Does C support this form of programming? Yes, there are function pointers and it is possible to carefully construct a function as non-mutable. Is it easy to program this way in C? Heck no! There are no tools that catches the mistakes you make, that forces you to write purely functionally.

This is why C is not a functional language, even though it is a very good language (it’s the one I’m most familiar with). Sooooo… yeah. :slight_smile:

4 Likes

That just boils down to whether you differentiate between “functional programming” and “purely functional programming”. In case you do not, then only pure languages need apply: Scheme, Clojure, Haskell, Elm. Probably a few others I’m missing.

If you do, then anything that allows treating functions as first class citizens qualifies, that arguably includes the like of Java and C#.

Personally I do not differentiate, ergo: in my mind Java is not a functional language, nor is JavaScript, nor C#, nor Ruby, nor Python, nor Common Lisp.
Those language allow functional style, but they do not require it, nor is it the “default”.
Immutability by default (even if by convention), for me, is a requirement for a language to be considered functional in even the loosest sense (eg. Scala, being an OO/FP hybrid).

Eh, I wouldn’t use JavaScript as a good example of a functional language as it is imperative/OO first, with the option to do FP.

A purely functional language, I’d argue, is probably a much better start if one wants to really get into a proper functional mindset, as they will disallow side-effects, forcing functional style, unlike any of the hybrid languages.
All of the Scheme variants fall in this spectrum (since it’s what sets Scheme apart from other Lisps), including Clojure, but also Haskell, Elm, and if you really hate yourself XSLT 1.0 (I’m not even joking, and don’t ask me how I know, I don’t want to talk about how I got those scars … :wink: )

The difference is important since pure functional style requires quite a different (imho: better) approach to structuring code whereas all the languages that “just” include functional features do not discourage reverting to imperative habits when the going gets tough, thus hampering the learning process.

I’d also add actually mastering SQL to that list. Set-based programming is something most developers have trouble with, which is probably why there’s so much horrible SQL out there, and why cursors are so widely (ab)used.

2 Likes

Oh, I do not recommend JavaScript primarily as a FP language; I recommend it because it is accessible, because the way it is used requires the programmer to think asynchronous and because it actually has first-class functions, allowing for functional programming should you wish to do so.

Of the four I recommend, three will be able to land you a job, especially if you also learn some embedded programming (C ), game programming (C#) or HTML5/CSS3/React (IavaScript) with it. As for Python it is a very useful language but it is mostly used professionally in the realms of computer science or backends, where you will want to learn a whole heap of other technologies at the side.

I agree SQL is a very useful language to learn as well, but you gotta draw the line somewhere. If you are anywhere near backend / data collection, SQL is a great secondary language to learn; for frontend or game development, not so much. Unfortunately. Same thing with Haskell or any of the pure functionals - they are great languages, but have limited real world usage today.

1 Like

But so do C#, and Java…

Unless aiming for a job in a specific language I think that shouldn’t be too much of a consideration assuming there’s something else you gain from learning a language. In the case of FP that would be a proper understanding of a development paradigm, something hybrid languages are very unlikely to provide (my experience with devs and hybrid languages, even those edging more towards FP than others, would almost tempt me to say “not” rather than “unlikely”)

A language is a tool (and I wish they were treated more as such, rather than religions, but I digress), if you know one language within a paradigm picking up another one should be easy enough, at least in my experience.

Going from Java to C# is almost trivial, for example. The actual learning is in the libraries and tooling, barely in the actual language.
Going from Java to Clojure? Not quite as simple.

Hence why I always recommend people learn a language that fully embraces a paradigm rather than hybrids, at least if the goal is learning. Once you grasp the paradigm, learning a new language is generally not that hard.
It also guards against “rusting” into a paradigm. The number of devs I’ve seen that really couldn’t think of a problem in anything but an OO “mindset” (and before that I’d seen it with imperative when everyone was migrating to OO) has been quite staggering and hybrid languages don’t do anything to discourage that.

Furthermore if I see a CV with C#, Python, and JavaScript that’s not going to stand out all that much (depending on the rest of said CV, obviously).
Those are the languages that “sell” nowadays, same with whatever the hip frameworks “du jour” are (or whatever “sells” in the enterprise). If they have some stuff in there that proves they actually care about software development (or IT in general) beyond just “whatever lands me a job” that CV moves up in the stack, at least for me.

Python is the new Perl, pretty much. Used for scripting, and sysadmin stuff all over as well. Definitely a useful one to have some knowledge of.

Eventually you have to store data somewhere, and fear of SQL, and a general lack of understanding of databases (I mean, they are big, complicated, and scary, but also extremely powerful) has lead to many bad decisions in that realm.

Being actually good at SQL and grokking databases at a more than superficial level almost guarantees a job, and a very well paid one at that.

Sorry, but no. C, C# and Java does have function pointers, but functions are not a first class function in those languages. See:

https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function

JavaScript is not a purely functional programming language, but it has much better support structures for functional programming than C, Python or C#/Java. Same thing with asynchronous programming, you can do it in Procedural languages too, but it’s a lot more difficult.

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

1 Like

Please guys can we stop talking about the merits of functional languages and OOP languages? I don’t care about that and it is grotesquely off-topic. It’s a waste of forum space in this thread.

I tried to say it nicely before, but I guess I’ll have to straight up say it. I am not going to listen to anyone’s advice about what languages to learn. Thus all advice of this manner is straight up bullshit.

why exactly?

do you want to build stuff or keep glossing over theoretical stuff?

read this with a respecting tone

1 Like

In Python, specifically, there’s certain kinds of things that naturally lend themselves to being classes and having oop style code.
For example, things built upon dataclasses, various containers, complex interfaces/APIs.

Often times in Python people use classes where they’re not necessary, e.g. because they might come from a university research or Java background.

My guiding principle is to do loose coupling between pieces of code through minimal APIs . e.g. if you need someone to give you a callable - don’t ask for a subclass with a method on it – that kind of thing.

To me functional programming is kind of upside down. I don’t really get the appeal.

My apologies, the reason I brought this up is because of this paragraph:

It’s nearly impossible to explain the concept of OOP or FP to a point of deeper understanding without using a language that supports these features (And I do not think C or Python does that - C lacks classes and function overrides and Python lacks hard typing). All I can say is, make sure you understand the four defining principles of encapsulation, abstraction, inheritance and polymorphism, and find a few general Youtube videos that explain OOP in Python, maybe?

As for Functional Programming, well, perhaps this tutorial can shed a little light on the problem? Best Practices for Using Functional Programming in Python — James Le

2 Likes

Take a ‘Programming languages and compilers’ course at university.

You’ll learn more than enough about all things programming languages. At my university, you learned functional programming with Haskell. Here’s a Computerphile youtube video on Haskell and functional languages.

Functional is amazing. You can start telling the computer what to do rather than how to do it. In my opinion, it’s a programming technique that helps obscure mechanics even further than OOP. Which is great, cause I had writing boiler plate code.

1 Like

(I’m sorry, this turned out longer than I imagined. I hope you don’t mind my ramble…)

Honestly, the terms “Functional programming”, “Object-oriented programming” or “Procedural programming” are incredibly overrated:
You’d think you could apply them to or detect them in a project, or source file - well you can’t.
They’re more like a way of thinking about programs, they’re fundamentally not in programs.

More specifically, they’re called “paradigms” for a reason. If you google “paradigm definition” you get “a typical example or pattern of something; a pattern or model.”, and “programming paradigms” are exactly that:
Patterns that you can apply when programming.

You can think about the same program from a functional or procedural or object-oriented perspective.

What you get out of it depends on your knowledge on said paradigms, and what happens when you try to recognize the patterns said paradigms.

I know you’re not asking for a religious language war, it’s stupid, I agree.
But let me give you an example from a language that I’m very familiar with, Lua.
You can write programs in all paradigms in it, just like Javascript, Python, and a lot of other languages. Also it’s syntax is somewhat ideomatic which makes for good examples

A simple for loop for counting down “procedural style”:

function count_down(max)
  for i=max,1,-1 do
    print("i is", max)
  end
end

count_down(10)

Why is this “procedural”? It defines a procedure as a list of procedures, to be executed in order.

How could someone (mis-) treat this as functional? Well, count_down is a well-behaved function, with no side-effects*:
It always maps the integer max to the nil(nothing) return value. While typically not very useful in a functional programming language, it’s still valid.

And how is this object-oriented? Well, if you knew Lua, you’d know that this is essentially three things happening here:
We define a function, then add it to our environment(_G or _ENV), then run it.
The defining and adding it to out environment can be interpreted as adding a method to the environment, and running it can be interpreted as invoking that method on the environment. Not how I’d usually treat this code, but still valid.

A simple recursive function for counting down “functional style”:

function count_down(cur)
  print("i is", cur)
  if cur ~= 1 then
    count_down(cur-1)
  end
end

count_down(10)

Why is this “functional”? Well, it uses recursion and causes no side effects*.

How is this procedural? By default, after all we’re just defining a single procedure and then runs it.
Sure, this procedure does not cause side effects* and might be recursive, both things typically associated with functional programming, but perfectly valid in procedural programming(and both quite common in e.g. C).

A simple “object-oriented” example:

obj = {
  min = 1,
}
function obj:count_down(max)
  for i=max, self.min, -1 do
    print("i is", max)
  end
end
obj:count_down(10)

Why is this object-oriented? Well, we can see that we have a thing(call it object) that can both hold it’s specific state(the number min), and a function used to access that state (a “method”), typical object-oriented stuff.

Why is this procedural? Well, the object is in no way special, it’s just a regular Lua table, that happened to have a number value at the index “min”, and a function value at the index “count_down”. The function is in no way special, Lua is just clever with the use of the :: When defining a function, it adds a hidden first argument called “self” to a function. When calling a function from a table using : (instead of the normal indexing operation using .) it passes the table the function is found in as first parameter. As you can see, the function is in no way specialized to the table. It’s just syntax sugar. In fact, this below would be exactly the same to Lua:

local function my_count_down(self, max) (same function body as above) end
obj.count_down = my_count_down

In fact, Lua has no concept of object-orientation, it’s just easy to use in a object-oriented way.

Now, all these examples look very different, but fundamentally, they are the same program, at most it’s the implementation that differs. All examples will count down from 10 to 1.

They were written differently, with specific paradigms in mind. And maybe you can recognize some specific patterns in them to associate them with a specific paradigm. But that’s only useful as far as the found patterns help you understand, these programs “are” not “in” a paradigm - It’s merely useful to view them as such, sometimes.

All programming paradigms are useful, if applied to the right thing.
But as you can see in the examples above, if applied to the wrong thing they make things needlessly complex.

With regards to a recommendation on learning: While you can learn the patterns of these concepts in basically any order, I’d recommend this:
Learn procedural programming to understand how computers work. While programming is not bound to a specific paradigm, in a way computer will always be best represented as “procedural”. It’s a good first starting point, as it helps understand other paradigms.
Learn functional programming to become a better programmer. At some point you can basically implement anything in a procedural language, just not well. While you likely won’t be using functional programming for anything work-related(there are exceptions), functional programming is great for understanding algorithms and data-structures, which will help tremendously even with procedural programming.
To truly appreciate functional programming you also want to understand the implementation. Functional programming languages are usually easier to implement.
Learn object-orientation to be a productive programmer. The main advantage of objects is that you can easily chain them up to very large, complicated programs; They make organizing your code easy, they enable easy interoperability between implementations via interfaces, etc. - there is a reason a lot of large software is implemented in C++, Java, etc.
The reason object-orientation is so powerful is also the reason why it’s a bad start for learning and a bad fit for small-scale programs: It tries to hide complexity of the implementation. In a large program you want that, but in a small program you want to know what you’re doing, exactly.

I hope that cleared things up a bit and that you don’t mind my ramblings :smiley:
I’m very sorry for this wall-of-text…

* Keep in mind that I play it fast-and-loose with some of the terms used here, because they are: Truly side-effect free programs do nothing, if you think about it: Every kind of IO operation is a side-effect, including print. Depending on your definitions, just *starting a program* is a side-effect.

Also keep in mind that by no means I’m an expert

3 Likes

Just throwing some practical Java examples into the mix:

A procedural java loop would look like the following

for (int number : arrayFromOneToFive) {
    print(number);
}

I’ve left out some definitions, but I hope it’s clear what it’s doing. It’s looping over each number in the arrayFromOneToFive and printing each number. For each, it’s printing each number.

Java 8 introduced this notion of stream-ing, which is a functional technique to do the exact same thing.

arrayFromOneToFive.stream().map(print);

As you can see, I’ve done the exact same thing, I just don’t need to know about how for loops work. Java is doing exactly what I want it to do. I’m stream-ing each int and map-ing the function print onto in.

It also takes up way less space. It’s a clever one-liner.

That brings me onto another topic. Functional programming is often a stylish thing to do. It doesn’t actually work any better than using a for loop. It’s just far removed from the procedural machine code the Java for loop is labeling.

That’s just the foundation. With that you gain some interesting tools and insights.

  1. Higher order functions. - Functions that take functions as inputs - makes common data manipulation easier to understand in a succinct manor.
  2. Composable functions - lets you focus on dataflow, reduces intermediate variables, improves readability.
  3. Referential transparency - without hidden state, it becomes easier to reason about code segment behavior.

Random functions either include a seed, or input from the real world. (Type I/O in haskell)

I’m not as familiar with OOP, but I’ve seen code burried in so many layers of abstraction that it’s hard to tell what’s going on. But it isa way of trying to manage complexity by managing state and formalizing interfaces between it. At the end of the day it’s just a way to organize and propagate code and data.

1 Like
Meta

Ho boy, this reply lead me down a rabbit hole. I have written this out as an essay a few times, gone home and slept on it, came back this morning and re-wrote it again. Here is attempt 3 or 4 on it, without the whole lexer tokenization or abstract syntax tree generation bits.

There is a TL;DR at the bottom I promise.

State is the condition of your application at any given point in time. Consider the following:

foo = 1
foo = 2
bar = foo

State is simply the values of all the defined variables. At each step through execution the state changes. Some variables are undefined, some variables definitions change. The point being that “state” is a dynamic and abstract idea.

In this example, there is no delegation of responsibility of state. The values of all the variables are held in the global namespace and you have to juggle them all, keeping in mind memory leaks and carefully feeding the garbage collector.

As a slightly more complicated example, let’s say you had a class that would take a reference to a JSON string and allow manipulation to it as if it were a native (your language here) variable. Shameless plug for a library I wrote that does that, not in Python though.

myJson = '{"foo": "bar"}'
JsonManipulator(myJson).foo = "baz"
print(myJson) # (string) {"foo": "baz"}

In this example the JsonManipulator class is given the bare minimum input that it needs, but under the hood it parses the inputs and manages its own variables and references. From the programmer’s point of view, you are delegating the state of the logic defined in that class to that class itself.


Digging deeper, to fully appreciate state you must understand what makes up a programming language. Your code is made up of three parts. Values, Expressions, and Statements. Let’s do a quick recap of these three.

  • Values are just that, they are a literal representation of a value. Strings "foo" and numbers 42 and lists [], all scalar values.
  • Expressions are an abstraction on values. These can be assignmentsfoo = ... or operators foo <= ... or applications of values to a function foo("bar", 40 + 2). Expressions are abstract until parsed, at which point they are just regular values.
  • Statements are immutable instructions that control the flow of the program. These are the ifs’ and whiles’ and function declarations of programming languages.

Take a quick look at the below, it has all three of the above. See if you can point out each of them to yourself.

def add(a, b):
  return a + b
foo = add(1, 2)

As the parser for your language of choice runs through the script instruction by instruction, it has a table that it holds to describe what instruction it is on and what the values of all the defined variables are. This is the state, and it changes with every parsed expression.


Moving past all that, there is a big difference between OOP and procedural-style programming, and there is LOTS of room for them to work together.

For example, in a project I was involved with we had to pass data into a big machine-learning “black box.” This was a huge process encompassing many thousands (if not tens of thousands) of lines of code that took an input, set up the proper classifier, managed the dataset transformations, then applying the input to get the result. The final result looked remarkably similar to this:

function predict(string $subject) {
  return classifier()->predict(
    dataset()->transform(
      $subject
    )
  );

I got some comments after writing that, that this was a fantastic example of procedural programming and applauded me for avoiding the “evil ways” of the OOP. They seem to have glossed over the fact that there are at least two classes used right there in the example, but they liked it anyways because of the proper separation of concerns giving rise to a rather neat little bit of code with the spaghetti hidden deep in a src/ directory.

They liked it not because it did not use classes, although that is what they told themselves, but because of proper delegation of responsibilities and separation of concerns. There are no free-floating variables, no temporary pointers, no definitions getting in the way of the logic. It is all there, just hidden out of sight.

There is the mention of higher order functions by @WorBlux. This can be handy for passing state around, but will quickly turn into impossible-to-debug gore once you get more than two levels deep. Its like unleashing a self-driving lawn mower on the neighborhood, lots of potential but will inevitably run over the neighbors kid.


TL;DR

In general terms, and to sum up the entirety of the “procedural vs. OOP” argument:

Functions are a great way of saying “do x with y data” and objects are great for saying “represent x data in y way.” Anyone who says the later does not need the former, or that the former is better without the latter, is lying or doing it wrong or both.

4 Likes