Encapsulation isn't about privacy of data. Not even in a programmatic sense. It's about consistency of data within an object. For example, if you change a person's name, you MUST also change the initials. Encapsulation can force you to do both at the same time. To be fair, encapsulation can also be used to keep values opaque, that is, to prevent you from using an underlying value directly, but encapsulation is definitely not about privacy. It's better to say that encapsulation is to protect inner data (by controlling its mutability.) Don't be fooled by the keyword 'private'. It essentially means "you're not supposed to use me directly ".
This makes a lot of sense. It never made sense to me when I read that encapsulation is for privacy so that other classes cannot access it, they never say why would there be a need to be hide the data from other classes. Even in this video, the SSN example is so confusing as they say it should not public, as some other classes make abuse of this information. LOL. It's to maintain consistency, like checking if its in a proper SSN format before modifying.
Ahahah I just wrote a comment about that. Purporting private fields as having the ability to secure sensitive data is extremely dangerous as it could lead to vulnerable software in the future
Task 1: Write a moderately complex game with FP (nightmare). Task 2: Write a moderately complex data parser with OOP (nightmare). Know when to spatula and when to ladle.
Same. Two decades under my wings, and unless I'm implementing a data type/structure with internal logic, I'm unlikely to even consider oop anymore. And even then it's pretty much just for encapsulation.
That's confirmation bias. You got used to that paradigm, like it, and have been working with it for a long time. Hence, it must be the best paradigm. That's like me saying that Java is the best programming language because I have been working with it for over 5 years. While procedural programming might be the best for you, because you are experienced in that, and Java might be the best for me, because I have more experience with that. Neither is necessarily the best option, ultimately it depends on the project and the programmer skills. Think about it, if you try to work with Java without any prior experience with a similar OOP language (like C++), you would likely do a poor job. The same applies to me if I dare to write in raw C code.
functional languages are great at modeling the domain. OOP is better when the domain is inherently stateful and imperative tho, like some simulation. However, LISP is king for DSL and Haskell's monads are wonderful for this too
OOP is mostly putting state in box and mutate it inside by pushing buttons on the box FP is more like juggling with data, shooting them from cannon to cannon while every cannon make new version/transformation
Unironically, this is a likely future for programming. We define what we want some construction to be like logically, and then the computer figures out how to do satisfy the logical constraints. Maybe not for UI programming and stuff, but for algorithms its feels like a possible problem to solve, especially with how fast machine learning is progressing these days...
@@PaulSmith-gi5bf yeah, i was describing something more like model-driven programming rather than specifically "logic programming" (i.e. prolog). They are somewhat similar though. Prolog takes some logical rules as code and tries to find the set of inputs and outputs that matches the rules. What I was trying to describe in my previous comment was where you define generally what constraints you want your program to have, and the compiler _generates the program itself based on the constraints_. On a theory level, its kinda like if you tried to write a prolog program to generate a C program with certain constraints.
One interesting thing to notice is that the original ideas and philosophy of OOP is much more close to functional programming then many people would think, but the OOP largely used on the industry is very far way from the original ideas so we end up with one word that means to separate concepts.
I think there are some things you could learn about FP that might be useful for a video like this. Data structures are totally still a normal thing to have in FP, and encapsulation and privacy are also fairly common. So with your car example you don't necessarily have to use tuples - maybe that's easy in Python because they are compare by value and immutable, but many languages have data structures with named fields that are also compare by value and immutable. Haskell for example is based on the algebraic data type model. You can either have tuple-like data types, which are basically the same as tuples but with names associated, or you can have named fields. If you don't export the accessor for a named field from your module, it is private. Similarly, in Rust, though data structures can be mutable, you can design them not to be by simply not exposing any mutators and not making fields public. While Rust is firmly a mixed paradigm language, its data types are largely a functional design - there's no inheritance, comparison is generally by value, and in order to extend a type, you encapsulate it within another type. Interfaces are not a purely object oriented thing - Haskell has typeclasses and Rust has traits. Both take this quite a bit farther than object oriented languages usually do though, and you can implement typeclasses/traits on all types matching a specific pattern and implement that behavior on whatever can be deduced from the pattern you specified. Haskell takes it even a step further with higher kinded types - that's where you get things like Monads and Functors - basically types that implement a specific set of operations and contain other types themselves, so you can create entirely generic useful things that work equally well on Maybe as they do on lists. I would definitely recommend learning a functional language that has a strong type system, and not learn how to do FP in Python initially. You can totally do FP in a mixed paradigm dynamic language, but the concepts are a lot more obvious in a strictly typed one where functional programming is first class. I would also say functions being implemented in object-oriented style as methods doesn't make things less functional - you can have totally functional programming style methods in Rust that operate by value on self, and this is actually quite nice sometimes.
As a supplement, in python you can use dataclass(frozen=True) to create immutable named fields. Edit: P.S.: Strictly speaking all variables in Python are mutable, so dataclass(frozen=True) actually throws a runtime error when it is written, thereby simulating immutability.
I agree with you. In most cases I will stick to the framework I use and follow its design pattern. Sticking to a single programming language may make your more professional on it but it will make yourself harder to accept new paradigms.
i am a person who jumps from javascript to C to C# to C++ to checking how haskel works to checking how assembler works, to checking how angular.js works, to checking what RUST is, to checking what react.js, aaand... i never wrote any big projects yet. i can't find a job aswell because to find one i gotta be learning a single stack like react, typescript, mysql and node js, but what i'm doing is just spending 1-2 days on finding out what everything is and still not finding out
And here I am, just wanting to write imperative code. Though I'm aware of OOP (a bit of how Alan Kay intended, but mostly mainstream "Clean Code") and FP (I've learned Haskell for a parsing course), I still tend to fall back to basic functions and loops. Probably because Python's recursive support is lacking, and that's the language I work in daily.
Object-oriented programming with functional elements, you say? Minimize side effects, you say? Don’t mutate data when you don’t have to, don’t share state, you say? R U S T
FP would be "hard to scale" as you said if it contains only functions. But most FP languages offer types and traits (called typeclasses in Haskell). I prefer them to OOP classes.
You are correct. The definition I gave wasn't the exact definition but rather the most common use case of the definition, which allows users to manage how data is seen!
The key part of encapsulation in my opinion is boundaries. Boundaries allow groupings to be established, and that enables separation of concerns. You can group all of the related things within a set of boundaries while keeping the unrelated things separate. The existence of classes enables encapsulation because it creates a boundary between the data/methods within the class and everything outside. Private and protected access modifiers are another way of enforcing encapsulation. They take a theoretical/organizational boundary (the class) and make it a literal/functional boundary ie. no other piece of code can reach past the boundary and mess with the stuff in the class. The concept of "data safety"always confused me because I thought it meant that it was about making code more secure from hackers or something, but encapsulation does nothing remotely like that. It's purely about managing complexity by keeping uncoordinated developers from being able to do jank stuff that makes the code harder to maintain. If everyone could read each other's minds and understand the code perfectly, encapsulation would be totally usefulness.
@@elijahbuscho7715 thanks i read it all but remember even private and final member could changed from reflaction so hiding information not mean encapsulation.
A small suggestion to make sound in your videos better. You have quite noticeable reverberation in it (echo like in church). To decreade it and make your voice clearer 1) Hold mic closer to your mouth. This way you will increase the difference in the voice/room noise ratio and you voice will be more dominant. 2) Reverb is just a reflection of sound (mainly from walls), so to prevent it from appearing, the sound wave has to not hit the walls. Having something front of you (like blanket) will help a lot. The room you record in sounds quite empty in general, so having a lot of items in it will also help, but recording under a blamket would help the most :D
In my opinion, Rust is a balanced language in terms of orientation. It takes a lot of inspiration from the functional programming world but does not give up on practicality with structs and impls.
In your needs_maintenance function @6:07 drop the if clause and simply “return car[1] > 100”. You don’t need to write return statements giving the literal when your calculated value is the same, or just the negation.
Slight nitpick, but at 4:57 you can shorten it to just: return car[1] > 100 A very well-put-together presentation, makes me want to start doing FP again
I totally agree, You should never stick just to a single paradigm. For example, I use OOP only when I know it could be possible that I would need to create a new instance of the class. But if I have something like the Core of the program, that shouldn't be a class, since never should you have to instantiate a Core of the program twice in the same program
In my understanding, OOP is about data abstraction, "gluing" data and its operations together to form objects with an internal state; these objects can be either mutable or immutable. Functional programming, on the other hand, is about using functional abstractions and avoiding mutable state. Both functional and data abstractions serve the purpose of making code easier to understand (you can think in terms of simple interfaces instead of complex, detailed implementations) and easier to modify (the implementation is isolated from other parts of the program).
I love that you say "FP if well written, can be extremely elegant" and proceed to show an example of non-FP program where Python's sorted function has a side effect, as it changes the list in-place :D All good, though, the points are the same, and you did give the disclaimer
You can essentially model functions the same way as classes. The car maintenance example can be done functionally by having a createCar function which can be passed into driveCar. driveCar can then return the new state of the car, including whether or not it needs maintainance. Additionally, you could model it closer to how you did it, but have whatever function be used to drive the car return if it needs maintainence.
I cannot find the source on youtube anymore, but I really liked the video which described that the nature of OOP are not those 4 pillars since those 4 pillars are just measures to achieve the core intention, which is to simulate the design that multicellular organisms follow, which goes like this: While all cells receive the same signal, each biological cell changes it's behavior autonomously, which allows complex designs of multicellular organisms. You can think of cells as classes, incoming signals as calls to the methods and their ability to intercept signals as interfaces. I think that the success of OOP as dominant paradigm (And believe me, python is not a functional language, try Clojure/LISP) is achieved thanks to that successful design that allowed managing complexity not even in our code, but in our very bodies as well.
I think what you’re getting at is that object oriented programming allows for a degree of abstraction not possible with traditional, procedural programming. It allows an implementation to own how it’s handled rather than the caller of the implementation needing to know what do with it. Functional programming allows the same thing by using higher order functions that return closures, encapsulating the data passed in and exposing a function that the implementation owns to access it. In fact, you can implement true object oriented programming principles like message passing very easily in FP, which would be a challenge to do in any C style, statically typed OOP language.
@@Biriadan but why? OOP is much closer to the way regular people think I appreciate FP because it's much closer to the mathematical thinking, but unless you are working in some kind of high load computing domain, it's redundant, because you will just end up with premature optimization
@@vadimemelin2941 I’d say pure FP is closer. A variable is a stand in for some other value passed in, not a position in memory where a value is stored. Functions are a single expression not blocks of statements, and guarantees to be deterministic. The equals sign is equality and not assignment, which doesn’t exist in pure FP. It’s just tough to think in a non imperative mindset after likely decades of approaching software development that way. But the benefit of completely eliminating mutation cannot be overstated. It makes code so much more readable since any value being used is being created in a single place and will be *exactly* what is used later. Nothing can change it between its creation and usage. It eliminates all bugs from having data referenced by multiple objects. Nothing can change the underlying data so it becomes irrelevant what’s able to access it. An entire class of bugs from aliasing can be completely ignored. It’s as freeing as going from manual memory management to garbage collection. It allows you to write code that’s more reusable since there’s a greater emphasis on writing functions that’s can operate without even knowing what they’re operating on. The code to take a list of functions and compose them all can have most of its logic reused to write a function that reverses an array or finds the max value in a tree. And there’s certain things that are just an absolute struggle to do in OOP languages but are trivial in FP. For instance, writing a function that takes a function and a variable and returns a new function with its arity reduced by 1 and the value already applied. For instance, imagine I want to add 1 to every element in a list. In lisp this would be written as: (map (partial-apply + 1) my-list), where partial-apply is (define (partial-apply fn arg)(lambda args (apply fn (cons arg args)))). And without higher order functions and a true OOP approach the only way to do that would be to do a for loop over the array or a custom add function that contains a for loop. But those for loops and control flow operators will be everywhere no matter what, and FP lets you abstract away iteration and other control flow elements.
I love the FP approach, I try and use it in most of the time because I think it’s the most readable and really helps when working with js because you can handle all of the weird type cases effectively too. ( though ts would be better in that respect )
Just to warn you, personally I believe procedural languages these days like Go, or Rust, and whatever else you may find, are much better than object oriented programming and they subsume all the features you could want. If you want to see my explanation, continue reading. Let's talk about the details of object orirented programming versus functional programming. Object orientation -- as you've already described -- is popular for mainly four things. 1. Inheritance 2. Encapsulation 3. Abstraction 4. Polymorphism Of course, if you're one who has already looked at many other paradigms, you'll notice that OOP isn't the only one that implements all of those. You'll also notice that OOP languages have some of the most error-prone and confusing behavior when it comes to those things, and especially introducing term confusion for how these things work. About inheritance, it's one of the first experiences people have with the more general concept: "ad hoc polymorphism". I'm kind of surprised inheritance is still praised these days especially with it's infamy, but maybe it's just because I live in a bubble. Let me introduce to you what I think is one of the biggest flaws of OOP. You define a class that you intend to eventually extend. You then, later on, extend the class, by making another class that inherits it. You might not notice, but this is one of the first bad design decisions that many people committed, a popular example is Minecraft. And the problem here is that the child class is now coupled with it's parent class. If you change the behavior of the parent class, then the child class' behavior will also change. You might not think that's bad, or you might say that this is an inherent problem with ad-hoc polymorphism, but let's disprove that. First, if you don't think it's that bad, I'd ask to imagine needing to keep this in mind when working with a production project. It's very easy to change things around while forgetting or missing the child class, and eventually causing harmful bugs. But if that's not enough for you, inheritance usually isn't just done once. Usually there are tons of chains of inheritance in programs, and once another class extends that parent class, you'll see how problematic that becomes. And even worse, in the projects where there are long chains of inheritance, changing something in the middle of the chain is extremely anxiety inducing, because you have no clue what can go wrong. There are tons of names for tons of issues in inheritance: The Yo-Yo problem, The Fragile Base-Class Problem, The Diamond Problem, and there are likely other things I'm forgetting. Secondly, if you think that this is an inherent problem in ad-hoc polymorphism or that inheritance should be used in moderation to avoid this issue, I advice you to look into Go and Rust's ecosystems. Those languages replace inheritance with interfaces and traits, respectively, and suffer almost none of the issues, and they get to use those features much more which lead to more concision and power. Not only that, but also even *in* the languages that mainly use inheritance, like Java, or C#, or C++, you're mainly advised to use *composition* rather than inheritance when possible. Okay, so maybe this was proved to you. "Well, fine! Inheritance is not that great, but you've still got everything else OOP does!" First off, thank you for giving up the thought that inheritance is flawed, many people don't even go as far as to admit something that's done badly in their preferred paradigm or language is that bad. And secondly, please continue reading. With encapsulation, I think I'd like to gloss over this point quickly because I don't think there's much to talk about -- visibility and modules already subsume this feature very easily. Modules or even just allowing selective visibility in languages just completely gets rid of any need for classes to encapsulate state or invariants. I don't really know what else I have to add here. Well, maybe that, as you mentioned, OOP encourages a more annoying way to write code and organize it, meanwhile modules can be used in any way, but most people split them based on purpose, rather than splitting files so that every file has one defined structure and it's methods. So, let's go to abstraction. Surely that's the thing OOP does the best? I would argue no. I think OOP does this extremely badly, in that it is barely any form of abstraction over normal prodeural programming. But people are sort of *illusioned* that OOP is actually more abstract. I don't really even understand what sort of abstraction people think OOP brings to the table. I suppose bunching up relevant data and code in one place? But that's what modules are! And even if said language doesn't have moudles, even some languages that are only file based allow abstraction. The sad truth is that classes, and inheitance, are actively harmful in most situations. I mean, look at Java. You have abstract classes, interfaces, normal classes, public classes, private classes, child classes, super classes..... and all of that is just so much, yet it actively harms people's way of writing code. And also, the builder pattern is one good example that OOP languages mostly lack abstraction and have to work around this issue. Maybe polymorphism is the one pillar in OOP that makes it all worth it? I assume you guessed it, but my answer is no. Polymorphism is already very easily and readily available in almost all languages, including C, using void pointers. Dynamic languages "get it" by default because they treat every type the same, so all of the functions are polymorphic (except they break when used with the incorrect type). Golang has polymorphism. Rust has polymorphism. I'm not quite sure what else I have to say about this. Okay, so maybe the issues that exist with procedural programming and functional programming are why we use OOP. Maybe it's picking the least painful poison? Hoooo boy. That's very far from the truth. The thing is that OOP *is* just procedural programming except mostly with confusing and annoying things added on to it, and sometimes with more limitations, that by far make it extremely difficult to reason with your code, read, and write it. I have already mentioned Go and Rust many times, but they are a very clear example of this. They are languages that are more minimal in terms of needed knowledge to use, yet are much more powerful, concise, and abstract. (And no, Go and Rust are not OOP) But what about functional programming? Surely the issues around it make it as unusable as we thought it would be? It turns out, also no. Functional programming is probably one of the paradigms, where, if you try to look for lanuguages that advertise themselves as such, is one of the paradigms that lean more to concise and correct language structure than the OOP world. Functional programming is by far more abstract, more convenient, and less error prone than anything in OOP land. "But what about the problem with FP as provided in the video?" That part is my least favourite part of the video and is extremely misled. The author seems to think that in functional programming, one cannot have data structures. Let me provide an example in haskell, which is completely functional: ```hs data Car = MkCar { needsMaintenance :: Bool, hoursDriven :: Int } drive :: Car -> Car drive car = if hoursDriven car > 100 then car { needsMaintenance = True } else car { hoursDriven = hoursDriven car + 10} ``` or implemented another way ```hs data Car = MkCar { needsMaintenance :: Bool, hoursDriven :: Int } drive car@Car{..} | hoursDriven > 100 = car { needsMaintenance = true } | otherwise = car { hoursDriven = hoursDriven + 10 } ``` And one last thing: Functional programming *can* have object oriented programming put into it. As far as I know, any paradigm can be used as any other paradigm. Meaning you can write in an OOP way in Haskell, you can write in a procedural way in Haskell, etc. What matters is what the language encourages by default and I much prefer what Rust and Haskell encourage. And with Rust and Haskell you can have practically everything in any other language. "But what about performance? I've heard functional programming has really bad performance!" Not very true! Functional programming languages like Haskell are about as performant as Go and Java, and you can even get Haskell to perform close to C. Furthermore, newer languages like Roc are also getting very speedy. That's also completely beside the fact that functional programming is more efficient in languages that are *optimised* for functional programming. Meaning no matter how you look at it, writing something functional in Java isn't the same as writing functional code in Haskell, or Roc. And to hammer even more on how annoying most OOP languages are, all you need to do is look at a feature as basic as generics and inheritance. Let's take C#, Java, and C++. In C#, any generic piece of code leads to runtime dispatch of a function, which is bad because since the function isn't known statically, it cannot be inlined or optimized by the compiler. In Java and C++, calling a method over an object leads to dynamic dispatch most of the time, which even makes them very close to being dynamically typed than statically typed. Thanks for reading.
"And one last thing: Functional programming can have object oriented programming put into it. As far as I know, any paradigm can be used as any other paradigm. Meaning you can write in an OOP way in Haskell, you can write in a procedural way in Haskell, etc. What matters is what the language encourages by default and I much prefer what Rust and Haskell encourage. And with Rust and Haskell you can have practically everything in any other language." This is not correct. You can't have OOP in Haskell. I know you'll say "yes you can", but even the Costore Comonad (which is what objects are mathematically according to Luca Cardelli et al.) has to use the Monadic interface in Haskell - meaning you can never bypass the monadic interface and directly access costore comonads, i.e. objects. Just like in Java and C# and Ruby, you can never have a function - even "standalone methods" or "lambdas" are "lowered" or compiled down to a method on an anonymous class. So you also cannot do FP in many OOP languages. The paradigms are simply exclusive. This thing about wanting to say "you can do one in the other" is simply an attempt to appease everyone and stop fights. But it is pointless as people who want to do OOP want direct access to objects, and people who want to do FP want direct access to functions, and telling OOP people to go through monads and telling FP people to go through instance methods is not going to cut it, ever.
@@jboss1073 No. It isn't a way to appease everyone or stop fights. I firmly believe that OOP is completely useless in every situation and only serves to bring harm. Furthermore, the costore comonad doesn't "have to use the monadic interface" in Haskell. Comonads are not monads. They can't use the monadic interface, fullstop. This is an extremely idiotic thing to say and it seems like only someone who hasn't really used Haskell would utter. Secondly, we already have costore comonads that are very similar to OOP and don't use any sort of monadic interface: lenses. Thirdly, we have OOP in the form of monads as well which can use the monadic interface. Just because it is monadic doesn't mean it can't be OOP-- once again, that's a very idiotic thing to say. Monads are an extremely generic thing and just about everything can be made a monad. Objects are such thing. Simply because you use Monads along with objects doesn't mean that you don't have OOP. If that was the case, then JavaScript would be FP rather than prototype/object-based. But it isn't. And it is still OOP despite emulating a weak form of monads using promises. I highly suggest you don't respond and do more research for this rather than talk about things you have no clue about. The things you've just said are so obviously wrong it's laughable. I suggest looking up "lenses are costate comonad coalgebras" to get a grip on exactly how wrong you are. Have a good day.
@@ribosomerocker Luca Cardelli's talk "1996-10 A Theory of Objects (OOPSLA Tutorial)" on slide 45 says: "The only operations on objects are: ~ method invocation, ~ method update." Hackage's Comonad page says: "There are two ways to define a comonad: I. Provide definitions for extract and extend satisfying these laws:" Extract is invocation, extend is update. Hence, objects are comonads. Comonads are dual of Monads. So, Comonads do not use the monadic interface in Haskell. I was wrong. However, dealing with Comonads in Haskell is still similar to dealing with Monads, in such a way that the codo-notation package exists, acknowledging that codo is "analogous to the do-notation for monads.". Hence, one could correctly say that doing OOP in Haskell utilizes an interface analogous to the monadic interface. So I was missing the word "analogous" in order to make my phrasing that "Comonads/Objects use the monadic interface" correct. Not far from the mark, I think. "Secondly, we already have costore comonads that are very similar to OOP and don't use any sort of monadic interface: lenses. " Well, the package lens uses Control.Monad.Error.Lens for its errors - so if you want to catch lens errors, you are forced to use the monadic interface. Hence, your statement that "lenses don't use any sort of monadic interface" is incorrect. "Just because it is monadic doesn't mean it can't be OOP" Actually, it does mean just that. OOP is the dual of FP - just like, analogously, comonads are the dual of monads. So to have "monadic OOP" is just as oxymoronic as having "comonadic FP" - a mathematically impossible proposition. What you are talking about is "emulating objects monadically". But those aren't mathematical OOP Objects according to Luca Cardelli et al. since mathematical OOP Objects are always comonads and no monad is a comonad (in fact the opposite is almost true, as every coalgebra gives rise to an algebra, which is why you're thinking that OOP can be done within monads). "Simply because you use Monads along with objects doesn't mean that you don't have OOP." Yes, it does. Again, if you mix monads with objects, you only have "make-believe objects" but not the real "Luca Cardelli's comonad-equivalent objects" as mathematically defined. " If that was the case, then JavaScript would be FP rather than prototype/object-based." JavaScript is not declaring itself based on either algebra or coalgebra. None of its purported abilities come from a theoretical, mathematical basis. Hence, JavaScript resists any academic labeling with regards to its paradigm. Only informally can you assign a paradigmatic label to JavaScript. JavaScript is indeed "prototype-based" (it changed a bit with classes) but it is not interested in academically defining what that means. So we shouldn't include JavaScript within this discussion. "I highly suggest you don't respond and do more research for this rather than talk about things you have no clue about. The things you've just said are so obviously wrong it's laughable." Too late, I already replied. And it looks like the things I said were not "so obviously wrong" as you thought before. You're still coming across as someone who desperately wants to place OOP in the same category of respect and usefulness as FP. Just like most OOP programmers who are insecure about their choice of paradigm. The reason I say this is because you're here trying to argue that OOP can be done monadically. But that is a contradiction in terms, to which you're ignorant because you think OOP is just a "vague idea" that can be "shoved" into any algebraic structure. This is you confusing coalgebras with algebras. Just because the behavior of coalgebras in comonads can be emulated monadically (every coalgebra gives rise to an algebra so this is not surprising) you therefore think that OOP and FP are compatible. They're not. They're duals of each other. OOP is FP with the diagram arrows inverted. It is literally the opposite. So OOP cannot be done in FP and vice-versa. Unfortunately for types like you who keep trying to make everything "harmonic" telling people that opposite paradigms can work together. You're the pseudo-science doctor of programming. Keep your last answer up to show everyone how little you know.
I recently had to maintain a script written using the functional programming paradigm (in a multi paradigm language) to handle some basic operations. There was a bug occurring somewhere and I wanted to inspect the values in the circuit. Went something like: "array = (startArray || []) |> dedupe(flatmap(foo(bar(baz))))" It took me 30 minutes to reason about the control flow. To unwrap the steps in my head, I had to hold a imagined stack of all the steps in reverse. Sure I could run unit tests to probe - but sometimes you just want to glance at code - particularly if you don't have the environment set up and it's a quick fix. It is certainly concise and clever keeping all of that logic on one line, but it's exhausting unwrapping it to find a bug or change. So I rewrote it, expanding it into a verbose, boring, procedural control flow - using a Set to deduplicate entries, nested for loops to mimic the flatMap, etc. I added additional logging and safety guards then found the bug. Though it probably looks offensive and old fashioned to anyone reading it, it's impossible to get lost because all of the behaviours are explicit and self documenting. Regardless of whether you come from a JavaScript or C background - you would understand the procedural control flow. Lastly, when emulating the circuit in your head, you don't need to hold as many steps so it's harder to get lost in thought. IMO I'm open to FP and I do think it has its merits, but in my experience, it's sort of a meme and not suitable for most use cases.
It is true that FP is harder to comprehend. This is because there are fewer grammatical constructs: just one concept of recursion instead of three different loops, and soon. And our natural languages have lots of grammatical constructs for a reason: our minds need them as guides! But I still prefer FP because it lets me define new concepts without grammatical quirks. After all, every word can be verbed!
@@mattinykanen4780 I don't think that @DavidAlsh is too dumb to “comprehend” FP, as you insinuate. He points out real problems that happen if you code in the Real World(TM), with stake holders, projects owners, etc. who want you to get the job done in no time. - On a deeper level, the point is that computer architecture is by design imperative, so there will always be a huge gap between FP and the actual code that is generated. And, yes, you have to sometimes inspect that generated code (in the Real World(TM)). This is where being able to debug code in a traditional way becomes essential.
@@8BitInsekt I am sorry about the alleged insinuation; none was intended. I was just excited about the book "The Programmer's Brain" by Felienne Hermans which I had just read.Its neuropychological view of programming, coupled with my own superficial misunderstanding on the principles of both human and programming languages were behind that brain fart of mine. Funnily enough, just last night I read "Intermediate Science Knowledge Predicts Overconfidence" by Carmen Sanchez and David Dunning (Trends in Cognitive Sciences. 28(4) pp. 284-285, April 2024) - yes, _that_ Dunning. Your comment came therefore just in time: Without it, I might have remembered that article as "_they_ are overconfident" but thanks to you setting me straight I will remember it as "_I_ was overconfident" instead. That said, it will still stand my ground on the original issue: there is _no_ completely seamless multi-paradigm programming language. Either the seams show in the language itself or in the mind of the programmer, but they are there. I moreover believe that this mismatch will be inevitable. Luckily my brand new Boston Scientific Vercise Genus R16 brain diaper will block such secretions from now on.
If you do FP right, OOP (minus inheritance) should fall out as a special case where you combine reducers to achieve your goal. Technically FP can also fall out of OOP as a special case where the messages and DTOs hold all the data and you overwhelmingly use stateless strategies to describe behaviour, but without lambdas it's ridiculously verbose. C++ had Comparers before they budged and introduced lambdas.
Easily modeling a domain is absolutely one of the strong suits of FP - most FP languages have algebraic data types which make modeling a much more straightforward task than figuring out a complicated object hierarchy, especially in languages which don't support multiple inheritance. Learn about modeling a domain with product and sum types.
Code Report has a video on how he makes slides like this if your interested. And now with power point using more AI, I am guessing these transitions will get even better.
I like OOP. I don't like to be forced to do it exclusively without free methods (*cough* Java), but I like it. But Inheritance and choosing that as foundation for polymorphism? Yeah that stuff can burn in hell.
Java has first class functions, which is what i assume you mean by 'free methods'. (a 'method' not tied to an object is a function so a 'free method' makes no sense)
@@adambickford8720 I meant a freestanding function (free function). Yeah, free method isn't really a term. Java doesn't really have these. Everything has to be a class, so instead of, IDK, defining "pub fn euclidian_distance(start_pos: Position, end_pos: Position): f64" in a file called helper.[programminglanguage] I've got to write "package utility; public class Utility { static public double euclidianDistance(Position startPos; Position endPos) }" - the modern language example also has nicer namespacing by default. That Java - and C# too - forces you to stick everything in a class is annoying. The only reason I can come up with for it is because they wanted to stick with exclusively dynamic dispatch, but, I mean, Kotlin has freestanding functions on the JVM, just add a bit syntactic sugar if you need your dynamic dispatch because your runtime can't do without. Kotlin btw is (close to) how to do a proper modern OOP language. Classes are closed by default, methods final, you don't have the awkward differentiation between Wrapper classes and primitives which are the one thing without methods because dynamic dispatch, you have methods to deal with "encapsulation" in corporate - getters and setters - without having to add additional tooling - Lombok - or manually maintaining a shitton of getters and setters that just serve to OCCLUDE the ONE time where that approach was needed, it ACTUALLY HAS A TYPE LEVEL CONCEPT OF NULLABILITY JAVA. Yeah, that last one makes me really mad. (Almost) every method call in Java is a Null exception just waiting to happen.
@@adambickford8720 I'm pretty happy with how Java supports lambdas, but it's not quite as smooth as languages designed with first class functions in mind. Java has to have it as a hacked on feature where each lambda is a class behind the covers. As I understand it's related to why lambdas can only access variables in the closure that are effectively final. They also had to add all the function interfaces like Function, BiFunction, Supplier, instead of having a type system that actually supports higher order functions. I use Java daily though and considering the constraints they're under, I think the language designers did a great job adding even this level of support for lambdas.
@@telaferrum Yes, it's an implementation hack similar to 'erasure' in generics. Awfully silly you can't cast the exact same function body literal between 2 different compatible functional interfaces. And yes, theres a Java tradition of providing the minimal language changes to support a new clunky library api. It took them a decade to give us collection factory methods instead of literals? Optional vs a simple `?.` operator? Seriously, no Tuple? All that said, they are most certainly first class, higher order functions. Its like watching your grandfather ride a dirt bike; really janky and on the verge of something terrible... but he's doing it so far.
I think OOP just has too many footguns. Inheritance sounds good but get extremely hairy very quickly. More FP stuff reduces that footgun significantly. Can also do OOP with more composable structured than inheritance though.
the thing about java i like, the new versions of java provide cool things for example records make u get rid of boilerplates, also stream API which opens the door of functional programming in java
I am a FP lover and I uses classes mostly just to build FP constructs like custom functors or quasi-monades and stuff I am using TypeScript and NodeJS and I will agree that I would need to be able to help myself with an hybrid mindset it is probably difficult for anyone to be able to balance both paradigms and make use of them efficiently I am also curious about what others paradigms should be on the top of the list… I am not a purist so yeah for me using classes to define functional programming constructs is still functional programming and I then probably would have to think about what I would be calling strictly OOP in a similar manner where I would be using functions to help myself in a FP style but in an overall OOP mindset 😅😅😅😅
I like the approach of Erlang/Elixir, which is 'functional' when defining small, precise tasks, and uses the Actor Model when designing the whole system. It builds systems on asynchronous "nanoservices" that send messages and are supervised. This approach embraces the "let it crash" philosophy, allowing processes to fail and restart instead of trying to handle every possible error scenario.
statically typed functional languages allow you to avoid the pitfall you mention -- here's an example of the car class using OCaml modules: module Car = struct type t = { name: string ; miles_driven: integer } let drive t miles = { t with miles_driven = t.miles_driven + miles } let needs_maintenance t = t.miles > 100 end the module system and strong typing allows you to keep data together and act on data like you would with a class while maintaining immutability and functional guarantees. the problem with python is that you don't have a good way to group multiple bits of data together without using a class (maybe named tuples?)x
good video! i think learning to use multiple paradigms expands the repertoire of tools you can use to solve a problem and combining them can truly form some good code. it is probably necessary to talk about dynamic vs. static typing when mixing paradigms though, and striking a balance could be difficult. i like your use of python as an example, as it is illustrative of a good and balanced multi-paradigm approach.
It comes down to the requirements of the problem at hand and knowing which tool is the appropriate tool to use. It is also important to know when and where to use said tool and how to properly implement it within said language. This is applicable from Op or Machine Codes to Assembly all the way up the chain of abstraction to even Typescript. There are many paradigms out there and many different ways to solve the same problem and every approach will have pros and cons. The target of priorities ought to be Efficiency and Performance, Readability, Reusability, Reliability, and Easement of Maintainability and not necessarily in that order. The order may vary based on the specifications of the requirements of that job, task, feature, program, application, system, network, etc... Other than that, great video and keep up the good work!
@@Vexxel256 I also think that every modern language has a healthy mix. But the way Rust does implement it is way beyond what I thought a programming language was capable of. Just beautiful
@@jboss1073 it depend of the definition of OOP. According to wikipedia (en.wikipedia.org/wiki/Object-oriented_programming) OOP bring together data structures (generally product types) and function (named methods). Rust gives a way to perform the same result with associated functions (according to the uniform function call), impl and traits (one of the mechanisms that promote composition over inheritance en.wikipedia.org/wiki/Composition_over_inheritance#Avoiding_drawbacks). We also have abstraction, encapsulation (private members) and polymorphism (generics). Rust only support behavioral (and not structural) inheritance via traits and trait dependence. So it's true that Rust is not classic OOP but an OOP-dev can still work with it (using design patterns for instances)
"[Functional Programming] can be considerably harder to scale up". I've found the opposite to be true after almost 30 years of coding, but you do have to switch mental models and use different code organization techniques.
The car example could look nearly identical in FP as in your OOP example. Just replace "public void drive()" with "public static Car drive(Car car)" (assuming Java) Though mutability is cool when managed well, so I would do "public static void drive(Car car)"
I write rust and pride myself on writing immutable functional code. That being said, I still often talk about the larger parts of code with my team as being Classes, and having Methods, and Fields
@@ConcerninglyWiseAlligator when I talk about classes in rust, I mainly mean a longer lived struct with a constructor, state, and associated methods. nothing stopping you from having 2+ of these in a single module
im extremely artistic swap the r for a u and I tend to hyper over engineer. OOP helped facilitate that tendency and as a result i never completed anything. took me many years to ditch a lot of OOP nonsense and incorporate functional and procedural aspects. Once I did that I actually started finishing projects.
defacto, if you program well you tend to use 3 basic paradigms: structured, object-oriented and functional programming. I recommend reading clean architecture to find out if there really are paradigms. It's better not to mislead people
Your problem is you are still thinking in OOP terms while using FP. Why on earth would you find if car needs maintenance after the fact. As soon as car has driven more than the stipulated amount of time, it would go for maintenance. The flow would retain this information until it is used. If you keep thinking on OOP way, obviously FP will not make sense and it will happen often.
0:38 Not all OOP is class-based, for example JavaScript uses prototypes instead 2:00 That's a really bad example, "private" isn't for personal data you don't want others to know, it's for any field you don't want users of your class to modify without telling you (which is usually all of your fields, especially if you consider that changing a field's visibility after the fact will break a lot of existing code). You provide a getter for fields that users of your class should be able to read, and a setter for the ones they should be able to write, which lets you check that the new value is valid and adjust other fields accordingly to make sure the object stays in a consistent state. With a public field you can't prevent someone from going `obj.name = null;` and you probably don't want that to happen.
I love all paradigms of Coding, C# for OOP (my favourite programming language), C for procedural/imperative (because i kind of a masochist myself) and JS for FP (job related), i know there are more then these paradigms but i can tell you for sure that Prompt Paradigm (thanks to ai) will become a thing and i already hate it :(
1:51 this example is really bad for educational purposes regarding encapsulation, because you are making it seem like making fields private makes them "secure" from anyone reading them, which is not the case because everything resides in memory and can be read easily. you imply "sensitive data" should be in private fields, but this is fundamentally NOT what the private field is for. It is for giving the ability to modify/access a field exclusively to the class itself and nothing outside of it. A better word than "sensitive data" would be "internal data" that you dont want anyone outside messing with, and if they can it would only be through your defined methods like getters/setters.
Objects are beneficial to use when they can actually represent something real. Lets say a sprite in a 2D game. You store it`s data, have it`s properties like x,y location, methods like draw and update position, but everything else doesnt need to be an object and can be easly reprepsented with FP: get inputs, update objects positions, draw objects, set outputs, repeat. And it`s easier to understand the original idea behind OOP when you go through the history of programming languages, how they evolved and what OOP tried to solve. It was just an extension of structs and structs represented properties of some 'stuff'.
If you are programming games in OOP then your performance will be abysmal. The original idea behind OOP was to tell the compiler how to reuse code. It had nothing to do with structs.
@@karolstopinski8350 Functions allow humans to reuse code, but they don't tell the compiler that it can substitute one piece of code for another. Inheritance is meant to tell the compiler to automatically reduce the code used by a derived class to the code in the base class if that's sufficient. The idea is flawed, however, because it leads to forking in cases where the method of the base class is not identical to the method of the derived class. That's one of the reasons why debugging OOP is such a nightmare.
3:26 There is no side effect. `global_var` on line 3 is actually not global, it's local to `add` function call. That's because of assignment on line 3. It shadows `global_var` from line 1. You can write `global global_var` or `nonlocal global_var` between lines 2 and 3. This way assignment on the last line won't create a new local variable, but will use `global_var` from outer scope.
OOP was never about messages. The original use of OOP was to reduce memory footprint with automated code reuse. Then Kay entered the scene and was completely misunderstood by people who never listened to him. :-)
I'm going to be quite harsh, but honestly, this sounds more like a beginner's view of OOP and FP. At best incomplete, at worst completely wrong. private/public defines the scope in which the variables are accessible, it's not related at all to privacy or security. It would be completely normal to have the SSN be a public variable in a program that deals with SSN. Polymorphism also isn't an OOP thing. It was first brought on by FP and was added to OOP later on. There are also many more types of polymorphism than the simple subtype polymorphism you mentioned. For example, parametric polymorphism, which is the main type of polymorphism used in FP I also don't think talking about FP without having any experience using a FP language is a good idea. I am afraid that it is misleading more than helpful
This is making me feel like sooner or later someone will have the bizarre idea of just coding in a way that makes the code works and not worry about a specific constraining structure to adhere too.
FP presented here is just the functional style you can follow in many imperative languages. But, you simply can't reliably do purely functional programming if your language isn't designed for it. Similarly, to follow the logical programming style, you need a logic programming language. Functional programming is notoriously hard to define. Purely functional programming is way more clear-cut tho. Here is what defines it: 0) time and execution don't exist. there's only evaluation 1) basically everything is an expression You will usually pair that with: 2) each valid expression has a precise type 3) data types are expressions too and so you get polymorphism (generics, interfaces, etc.) (0) makes the code dead simple. The only complexity you get is what you write or import. Because of (1), everything composes. This removes boilerplate and allows for limitless abstractions. (2) limits that composing to reasonable and meaningful code. It let's you eliminate invalid states, write code that covers all cases and avoid defensive programming. (3) let's you encode invariants and other important properties within your abstractions. And in some languages it even allows you to write proofs about your programs. Funnily enough, when using a purely functional language, you can usually write certain parts in the imperative style. Like Haskell's State Threads or IO and State data types. Heck, it could probably model objects too! But I don't think anyone has bothered. So yes. Pick the best paradigm|tooling for the job, but also do explore your choices!
> Similarly, to follow the logical programming style, you need a logic programming language. Kanren begs to differ. It was ported to more languages than there are people using it.
@@mskiptr sure, when you have a proper meta-language, you can turn it into any language imaginable. Beauty of Kanren though is that it is built on the very basic conatructs that are present in pretty much any language, and yet they're sufficient to do logic programming. Of course, harder to do so with, say, lazy FP. Just interesting how not really alien the logic programming is, despite its alien appearance.
3:18 Shared State, Mutable Data, Side effects well... either "Shared State" or "Side effects" is redundant here - one the remaining two words suffice .... i think
Programing paradigms are Tools. One is a hammer, another is a plier and another is a screw driver. Thinks go wrong when you are limited to one Tools and try to hammer a screw.
literally just into 2nd module of MS Introduction to Python tutorial covering 'Introduction to object-oriented programming with Python ' so super noob at coding but already see the strengths of OOP as a paradigm but the OCD in me looks at Functional and goes 'so pretty' thanks so much for the vid - really added to what I am covering at the moment with OOP also.
You explained it very well. I just don't know for WHOM the explanation is. A beginner can't make heads and toes from the acronyms. Let alone understand the complexity of the problem itself. An experienced programmer is mostly defined by the language he uses (or is 'forced' to use). Maybe a reminder to think about the whole thematic? Well done, either way.👍👍👍
When I was a kid. Which is few months ago I thought "Python is the best programming language and functional programming is the best way of programming."
How do you make youtube videos like these? These animations are so clean and beautiful yet simple. Is it just video editing or are there other techniques involved.
U can use strict to model in FP and u don't model your problem u model the data and can use something like a changeset to set when the car driven is over 100 house need to do maintenance
Personally, I think its better to start with functional programming at least the style of it, so that you can understand the action part of the programming before understanding the structure part of the programming that OOP focuses more on
I will use elixir for the car exemple, defmodule Car do defstruct maintenance: false, hours_driven: 0 def drive(%{hours_driven: hours} = car) when hours > 100, do: %{car | maintenance: true} def drive(car), do: car end Yes, we don't need if in elixir but if you want: def drive(car) do if car.hours_driven > 100, do: Map.update(car, :maintenance, true), else: car end
Год назад
oops most important feature supposed to be messaging and not encapsulation
C# isn't a multi paradigm programming language. It is strictly an object oriented programming language. Even for a pure function, you will have to encase it inside a static object
Encapsulation isn't about privacy of data. Not even in a programmatic sense. It's about consistency of data within an object. For example, if you change a person's name, you MUST also change the initials. Encapsulation can force you to do both at the same time.
To be fair, encapsulation can also be used to keep values opaque, that is, to prevent you from using an underlying value directly, but encapsulation is definitely not about privacy.
It's better to say that encapsulation is to protect inner data (by controlling its mutability.)
Don't be fooled by the keyword 'private'. It essentially means "you're not supposed to use me directly ".
To be fair some of my professors also reffered to it as privacy in the same way OP did, which always confused me
This makes a lot of sense. It never made sense to me when I read that encapsulation is for privacy so that other classes cannot access it, they never say why would there be a need to be hide the data from other classes. Even in this video, the SSN example is so confusing as they say it should not public, as some other classes make abuse of this information. LOL. It's to maintain consistency, like checking if its in a proper SSN format before modifying.
Ahahah I just wrote a comment about that. Purporting private fields as having the ability to secure sensitive data is extremely dangerous as it could lead to vulnerable software in the future
Im a engineering lead and i would have hired u for this answer
@@isaiya6799 damn it sounds easy to get hired
Task 1:
Write a moderately complex game with FP (nightmare).
Task 2:
Write a moderately complex data parser with OOP (nightmare).
Know when to spatula and when to ladle.
Procedural remains king
@@roccociccone597 for a large majority of cases, I'll have to side with you
I had to remake mario in haskell for a FP course :(
Personally, I prefer neither declarative nor imperative programming, but *exclamatory* programming; shouting at the computer until it works!
underrated comment!
This is hilarous! 🎉
Now that's a paradigm I can get behind, as long as I can shout too.
I program in the KISS paradigm
😘
😘
😘
😘
💨
The more I work as a programmer (over a decade now). The more I'm sure that procedural programming is the best paradigm.
Same. Two decades under my wings, and unless I'm implementing a data type/structure with internal logic, I'm unlikely to even consider oop anymore. And even then it's pretty much just for encapsulation.
I feel like this is just an excuse for boomers to write shitty code (at least sometimes)
@@shner6742 good point
Now try to write somethig as good as the Unity game engine without OOP. Impossible.
That's confirmation bias. You got used to that paradigm, like it, and have been working with it for a long time. Hence, it must be the best paradigm. That's like me saying that Java is the best programming language because I have been working with it for over 5 years. While procedural programming might be the best for you, because you are experienced in that, and Java might be the best for me, because I have more experience with that. Neither is necessarily the best option, ultimately it depends on the project and the programmer skills. Think about it, if you try to work with Java without any prior experience with a similar OOP language (like C++), you would likely do a poor job. The same applies to me if I dare to write in raw C code.
functional languages are great at modeling the domain. OOP is better when the domain is inherently stateful and imperative tho, like some simulation. However, LISP is king for DSL and Haskell's monads are wonderful for this too
People acting like you can't combine those two, it's not exactly set in stone decision, you can always do what is appropriate for the situation
Bravo 👏
Finally someone gets it
As a matter of fact, the cleanest code I’ve seen leveraged both in different situations in the same codebase
OOP is mostly putting state in box and mutate it inside by pushing buttons on the box
FP is more like juggling with data, shooting them from cannon to cannon while every cannon make new version/transformation
We all know that Logical Programming is the true endgame of mainstream programming😂😂😂
😂
Unironically, this is a likely future for programming. We define what we want some construction to be like logically, and then the computer figures out how to do satisfy the logical constraints. Maybe not for UI programming and stuff, but for algorithms its feels like a possible problem to solve, especially with how fast machine learning is progressing these days...
Constraint logic programming
@@zyansheep that's not how logic programming works, it essentially solves a constraint satisfaction problem, which has a deterministic answer
@@PaulSmith-gi5bf yeah, i was describing something more like model-driven programming rather than specifically "logic programming" (i.e. prolog). They are somewhat similar though. Prolog takes some logical rules as code and tries to find the set of inputs and outputs that matches the rules. What I was trying to describe in my previous comment was where you define generally what constraints you want your program to have, and the compiler _generates the program itself based on the constraints_. On a theory level, its kinda like if you tried to write a prolog program to generate a C program with certain constraints.
One interesting thing to notice is that the original ideas and philosophy of OOP is much more close to functional programming then many people would think, but the OOP largely used on the industry is very far way from the original ideas so we end up with one word that means to separate concepts.
@@JorgetePanete tnx
I am no longer doing OOP since I switched to Clojure and I haven't looked back.
Clojure mentioned!
I wish more people discover clojure
I think there are some things you could learn about FP that might be useful for a video like this.
Data structures are totally still a normal thing to have in FP, and encapsulation and privacy are also fairly common. So with your car example you don't necessarily have to use tuples - maybe that's easy in Python because they are compare by value and immutable, but many languages have data structures with named fields that are also compare by value and immutable.
Haskell for example is based on the algebraic data type model. You can either have tuple-like data types, which are basically the same as tuples but with names associated, or you can have named fields. If you don't export the accessor for a named field from your module, it is private.
Similarly, in Rust, though data structures can be mutable, you can design them not to be by simply not exposing any mutators and not making fields public. While Rust is firmly a mixed paradigm language, its data types are largely a functional design - there's no inheritance, comparison is generally by value, and in order to extend a type, you encapsulate it within another type.
Interfaces are not a purely object oriented thing - Haskell has typeclasses and Rust has traits. Both take this quite a bit farther than object oriented languages usually do though, and you can implement typeclasses/traits on all types matching a specific pattern and implement that behavior on whatever can be deduced from the pattern you specified. Haskell takes it even a step further with higher kinded types - that's where you get things like Monads and Functors - basically types that implement a specific set of operations and contain other types themselves, so you can create entirely generic useful things that work equally well on Maybe as they do on lists.
I would definitely recommend learning a functional language that has a strong type system, and not learn how to do FP in Python initially. You can totally do FP in a mixed paradigm dynamic language, but the concepts are a lot more obvious in a strictly typed one where functional programming is first class.
I would also say functions being implemented in object-oriented style as methods doesn't make things less functional - you can have totally functional programming style methods in Rust that operate by value on self, and this is actually quite nice sometimes.
As a supplement, in python you can use dataclass(frozen=True) to create immutable named fields.
Edit: P.S.: Strictly speaking all variables in Python are mutable, so dataclass(frozen=True) actually throws a runtime error when it is written, thereby simulating immutability.
I have no idea what you mean by mutator in Rust, it’s my first time hearing it.
Thank you! This is great information! What do you think is the best functional language to learn to properly get a feel for functional programming?
@@codepersist I would recommend Elixir. From there it's also worth checking out Haskell and some Lisp language(s)
Not even a single pinch of condescence
Great comment for a newbie to Rust like me 😅
I agree with you. In most cases I will stick to the framework I use and follow its design pattern. Sticking to a single programming language may make your more professional on it but it will make yourself harder to accept new paradigms.
i am a person who jumps from javascript to C to C# to C++ to checking how haskel works to checking how assembler works, to checking how angular.js works, to checking what RUST is, to checking what react.js, aaand... i never wrote any big projects yet. i can't find a job aswell because to find one i gotta be learning a single stack like react, typescript, mysql and node js, but what i'm doing is just spending 1-2 days on finding out what everything is and still not finding out
It'll make you a frameworker more than a developer
And here I am, just wanting to write imperative code. Though I'm aware of OOP (a bit of how Alan Kay intended, but mostly mainstream "Clean Code") and FP (I've learned Haskell for a parsing course), I still tend to fall back to basic functions and loops. Probably because Python's recursive support is lacking, and that's the language I work in daily.
clean code is a lie... check out recent molly rocket video, it's a good un
Python has very good functional support, at least for iterables, specially using itertools.
@@imsamuka928 "Very good functional support" - until not long ago you couldn't print from a lambda in Python!
@@jboss1073 oh yeah, i hate python lambdas with my core. I imagine that whitespace sensitivity fucked the language designers pretty hard on this one
Object-oriented programming with functional elements, you say? Minimize side effects, you say? Don’t mutate data when you don’t have to, don’t share state, you say?
R U S T
FP would be "hard to scale" as you said if it contains only functions. But most FP languages offer types and traits (called typeclasses in Haskell). I prefer them to OOP classes.
The encapsulation definition is wrong it is not about hiding data but about be one unit all property and behovir in on place in the class
You are correct. The definition I gave wasn't the exact definition but rather the most common use case of the definition, which allows users to manage how data is seen!
Phrase "most common case of definition" makes me cringe as well
The key part of encapsulation in my opinion is boundaries. Boundaries allow groupings to be established, and that enables separation of concerns. You can group all of the related things within a set of boundaries while keeping the unrelated things separate. The existence of classes enables encapsulation because it creates a boundary between the data/methods within the class and everything outside.
Private and protected access modifiers are another way of enforcing encapsulation. They take a theoretical/organizational boundary (the class) and make it a literal/functional boundary ie. no other piece of code can reach past the boundary and mess with the stuff in the class.
The concept of "data safety"always confused me because I thought it meant that it was about making code more secure from hackers or something, but encapsulation does nothing remotely like that. It's purely about managing complexity by keeping uncoordinated developers from being able to do jank stuff that makes the code harder to maintain. If everyone could read each other's minds and understand the code perfectly, encapsulation would be totally usefulness.
@@elijahbuscho7715 thanks i read it all but remember even private and final member could changed from reflaction so hiding information not mean encapsulation.
A small suggestion to make sound in your videos better. You have quite noticeable reverberation in it (echo like in church). To decreade it and make your voice clearer 1) Hold mic closer to your mouth. This way you will increase the difference in the voice/room noise ratio and you voice will be more dominant. 2) Reverb is just a reflection of sound (mainly from walls), so to prevent it from appearing, the sound wave has to not hit the walls. Having something front of you (like blanket) will help a lot. The room you record in sounds quite empty in general, so having a lot of items in it will also help, but recording under a blamket would help the most :D
It is also possible to use plugins that can remove some of the reverb :)
In my opinion, Rust is a balanced language in terms of orientation. It takes a lot of inspiration from the functional programming world but does not give up on practicality with structs and impls.
But it ends up with a leftist managing team bent on pushing their agenda onto others.
you can do just:
def needs_maintenance(car):
return car[1] > 100
instead of 4:59
In your needs_maintenance function @6:07 drop the if clause and simply “return car[1] > 100”. You don’t need to write return statements giving the literal when your calculated value is the same, or just the negation.
Slight nitpick, but at 4:57 you can shorten it to just:
return car[1] > 100
A very well-put-together presentation, makes me want to start doing FP again
Comparing FP and OOP but using an object oriented language for both. Awesome.
I totally agree, You should never stick just to a single paradigm. For example, I use OOP only when I know it could be possible that I would need to create a new instance of the class. But if I have something like the Core of the program, that shouldn't be a class, since never should you have to instantiate a Core of the program twice in the same program
In my understanding, OOP is about data abstraction, "gluing" data and its operations together to form objects with an internal state; these objects can be either mutable or immutable. Functional programming, on the other hand, is about using functional abstractions and avoiding mutable state.
Both functional and data abstractions serve the purpose of making code easier to understand (you can think in terms of simple interfaces instead of complex, detailed implementations) and easier to modify (the implementation is isolated from other parts of the program).
I love that you say "FP if well written, can be extremely elegant" and proceed to show an example of non-FP program where Python's sorted function has a side effect, as it changes the list in-place :D
All good, though, the points are the same, and you did give the disclaimer
You can essentially model functions the same way as classes. The car maintenance example can be done functionally by having a createCar function which can be passed into driveCar. driveCar can then return the new state of the car, including whether or not it needs maintainance. Additionally, you could model it closer to how you did it, but have whatever function be used to drive the car return if it needs maintainence.
I cannot find the source on youtube anymore, but I really liked the video which described that the nature of OOP are not those 4 pillars since those 4 pillars are just measures to achieve the core intention, which is to simulate the design that multicellular organisms follow, which goes like this: While all cells receive the same signal, each biological cell changes it's behavior autonomously, which allows complex designs of multicellular organisms. You can think of cells as classes, incoming signals as calls to the methods and their ability to intercept signals as interfaces. I think that the success of OOP as dominant paradigm (And believe me, python is not a functional language, try Clojure/LISP) is achieved thanks to that successful design that allowed managing complexity not even in our code, but in our very bodies as well.
I think what you’re getting at is that object oriented programming allows for a degree of abstraction not possible with traditional, procedural programming.
It allows an implementation to own how it’s handled rather than the caller of the implementation needing to know what do with it.
Functional programming allows the same thing by using higher order functions that return closures, encapsulating the data passed in and exposing a function that the implementation owns to access it.
In fact, you can implement true object oriented programming principles like message passing very easily in FP, which would be a challenge to do in any C style, statically typed OOP language.
@@Biriadan but why? OOP is much closer to the way regular people think
I appreciate FP because it's much closer to the mathematical thinking, but unless you are working in some kind of high load computing domain, it's redundant, because you will just end up with premature optimization
@@vadimemelin2941 I’d say pure FP is closer. A variable is a stand in for some other value passed in, not a position in memory where a value is stored. Functions are a single expression not blocks of statements, and guarantees to be deterministic. The equals sign is equality and not assignment, which doesn’t exist in pure FP.
It’s just tough to think in a non imperative mindset after likely decades of approaching software development that way. But the benefit of completely eliminating mutation cannot be overstated.
It makes code so much more readable since any value being used is being created in a single place and will be *exactly* what is used later. Nothing can change it between its creation and usage.
It eliminates all bugs from having data referenced by multiple objects. Nothing can change the underlying data so it becomes irrelevant what’s able to access it. An entire class of bugs from aliasing can be completely ignored. It’s as freeing as going from manual memory management to garbage collection.
It allows you to write code that’s more reusable since there’s a greater emphasis on writing functions that’s can operate without even knowing what they’re operating on. The code to take a list of functions and compose them all can have most of its logic reused to write a function that reverses an array or finds the max value in a tree.
And there’s certain things that are just an absolute struggle to do in OOP languages but are trivial in FP. For instance, writing a function that takes a function and a variable and returns a new function with its arity reduced by 1 and the value already applied.
For instance, imagine I want to add 1 to every element in a list. In lisp this would be written as: (map (partial-apply + 1) my-list), where partial-apply is (define (partial-apply fn arg)(lambda args (apply fn (cons arg args)))). And without higher order functions and a true OOP approach the only way to do that would be to do a for loop over the array or a custom add function that contains a for loop. But those for loops and control flow operators will be everywhere no matter what, and FP lets you abstract away iteration and other control flow elements.
@@Biriadan Well, as the famous saying goes, FP would become a standard if someone didn't forget to put a ')' in the 70s
I love the FP approach, I try and use it in most of the time because I think it’s the most readable and really helps when working with js because you can handle all of the weird type cases effectively too. ( though ts would be better in that respect )
Just to warn you, personally I believe procedural languages these days like Go, or Rust, and whatever else you may find, are much better than object oriented programming and they subsume all the features you could want. If you want to see my explanation, continue reading.
Let's talk about the details of object orirented programming versus functional programming.
Object orientation -- as you've already described -- is popular for mainly four things.
1. Inheritance 2. Encapsulation 3. Abstraction 4. Polymorphism
Of course, if you're one who has already looked at many other paradigms, you'll notice that OOP isn't the only one that implements all of those. You'll also notice that OOP languages have some of the most error-prone and confusing behavior when it comes to those things, and especially introducing term confusion for how these things work.
About inheritance, it's one of the first experiences people have with the more general concept: "ad hoc polymorphism".
I'm kind of surprised inheritance is still praised these days especially with it's infamy, but maybe it's just because I live in a bubble. Let me introduce to you what I think is one of the biggest flaws of OOP.
You define a class that you intend to eventually extend. You then, later on, extend the class, by making another class that inherits it.
You might not notice, but this is one of the first bad design decisions that many people committed, a popular example is Minecraft.
And the problem here is that the child class is now coupled with it's parent class. If you change the behavior of the parent class, then the child class' behavior will also change. You might not think that's bad, or you might say that this is an inherent problem with ad-hoc polymorphism, but let's disprove that.
First, if you don't think it's that bad, I'd ask to imagine needing to keep this in mind when working with a production project. It's very easy to change things around while forgetting or missing the child class, and eventually causing harmful bugs. But if that's not enough for you, inheritance usually isn't just done once. Usually there are tons of chains of inheritance in programs, and once another class extends that parent class, you'll see how problematic that becomes. And even worse, in the projects where there are long chains of inheritance, changing something in the middle of the chain is extremely anxiety inducing, because you have no clue what can go wrong. There are tons of names for tons of issues in inheritance: The Yo-Yo problem, The Fragile Base-Class Problem, The Diamond Problem, and there are likely other things I'm forgetting.
Secondly, if you think that this is an inherent problem in ad-hoc polymorphism or that inheritance should be used in moderation to avoid this issue, I advice you to look into Go and Rust's ecosystems. Those languages replace inheritance with interfaces and traits, respectively, and suffer almost none of the issues, and they get to use those features much more which lead to more concision and power.
Not only that, but also even *in* the languages that mainly use inheritance, like Java, or C#, or C++, you're mainly advised to use *composition* rather than inheritance when possible.
Okay, so maybe this was proved to you. "Well, fine! Inheritance is not that great, but you've still got everything else OOP does!"
First off, thank you for giving up the thought that inheritance is flawed, many people don't even go as far as to admit something that's done badly in their preferred paradigm or language is that bad. And secondly, please continue reading.
With encapsulation, I think I'd like to gloss over this point quickly because I don't think there's much to talk about -- visibility and modules already subsume this feature very easily. Modules or even just allowing selective visibility in languages just completely gets rid of any need for classes to encapsulate state or invariants. I don't really know what else I have to add here. Well, maybe that, as you mentioned, OOP encourages a more annoying way to write code and organize it, meanwhile modules can be used in any way, but most people split them based on purpose, rather than splitting files so that every file has one defined structure and it's methods.
So, let's go to abstraction. Surely that's the thing OOP does the best? I would argue no. I think OOP does this extremely badly, in that it is barely any form of abstraction over normal prodeural programming. But people are sort of *illusioned* that OOP is actually more abstract. I don't really even understand what sort of abstraction people think OOP brings to the table. I suppose bunching up relevant data and code in one place? But that's what modules are! And even if said language doesn't have moudles, even some languages that are only file based allow abstraction. The sad truth is that classes, and inheitance, are actively harmful in most situations. I mean, look at Java. You have abstract classes, interfaces, normal classes, public classes, private classes, child classes, super classes..... and all of that is just so much, yet it actively harms people's way of writing code. And also, the builder pattern is one good example that OOP languages mostly lack abstraction and have to work around this issue.
Maybe polymorphism is the one pillar in OOP that makes it all worth it? I assume you guessed it, but my answer is no. Polymorphism is already very easily and readily available in almost all languages, including C, using void pointers. Dynamic languages "get it" by default because they treat every type the same, so all of the functions are polymorphic (except they break when used with the incorrect type). Golang has polymorphism. Rust has polymorphism. I'm not quite sure what else I have to say about this.
Okay, so maybe the issues that exist with procedural programming and functional programming are why we use OOP. Maybe it's picking the least painful poison? Hoooo boy. That's very far from the truth. The thing is that OOP *is* just procedural programming except mostly with confusing and annoying things added on to it, and sometimes with more limitations, that by far make it extremely difficult to reason with your code, read, and write it. I have already mentioned Go and Rust many times, but they are a very clear example of this. They are languages that are more minimal in terms of needed knowledge to use, yet are much more powerful, concise, and abstract.
(And no, Go and Rust are not OOP)
But what about functional programming? Surely the issues around it make it as unusable as we thought it would be? It turns out, also no. Functional programming is probably one of the paradigms, where, if you try to look for lanuguages that advertise themselves as such, is one of the paradigms that lean more to concise and correct language structure than the OOP world. Functional programming is by far more abstract, more convenient, and less error prone than anything in OOP land.
"But what about the problem with FP as provided in the video?"
That part is my least favourite part of the video and is extremely misled. The author seems to think that in functional programming, one cannot have data structures. Let me provide an example in haskell, which is completely functional:
```hs
data Car = MkCar { needsMaintenance :: Bool, hoursDriven :: Int }
drive :: Car -> Car
drive car = if hoursDriven car > 100 then car { needsMaintenance = True } else car { hoursDriven = hoursDriven car + 10}
```
or implemented another way
```hs
data Car = MkCar { needsMaintenance :: Bool, hoursDriven :: Int }
drive car@Car{..}
| hoursDriven > 100 = car { needsMaintenance = true }
| otherwise = car { hoursDriven = hoursDriven + 10 }
```
And one last thing: Functional programming *can* have object oriented programming put into it. As far as I know, any paradigm can be used as any other paradigm. Meaning you can write in an OOP way in Haskell, you can write in a procedural way in Haskell, etc. What matters is what the language encourages by default and I much prefer what Rust and Haskell encourage. And with Rust and Haskell you can have practically everything in any other language.
"But what about performance? I've heard functional programming has really bad performance!"
Not very true! Functional programming languages like Haskell are about as performant as Go and Java, and you can even get Haskell to perform close to C. Furthermore, newer languages like Roc are also getting very speedy. That's also completely beside the fact that functional programming is more efficient in languages that are *optimised* for functional programming. Meaning no matter how you look at it, writing something functional in Java isn't the same as writing functional code in Haskell, or Roc. And to hammer even more on how annoying most OOP languages are, all you need to do is look at a feature as basic as generics and inheritance. Let's take C#, Java, and C++. In C#, any generic piece of code leads to runtime dispatch of a function, which is bad because since the function isn't known statically, it cannot be inlined or optimized by the compiler. In Java and C++, calling a method over an object leads to dynamic dispatch most of the time, which even makes them very close to being dynamically typed than statically typed.
Thanks for reading.
Oh my god this man typed an entire essay
@@atiedebee1020 I'll do anything to educate
"And one last thing: Functional programming can have object oriented programming put into it. As far as I know, any paradigm can be used as any other paradigm. Meaning you can write in an OOP way in Haskell, you can write in a procedural way in Haskell, etc. What matters is what the language encourages by default and I much prefer what Rust and Haskell encourage. And with Rust and Haskell you can have practically everything in any other language."
This is not correct. You can't have OOP in Haskell. I know you'll say "yes you can", but even the Costore Comonad (which is what objects are mathematically according to Luca Cardelli et al.) has to use the Monadic interface in Haskell - meaning you can never bypass the monadic interface and directly access costore comonads, i.e. objects.
Just like in Java and C# and Ruby, you can never have a function - even "standalone methods" or "lambdas" are "lowered" or compiled down to a method on an anonymous class. So you also cannot do FP in many OOP languages.
The paradigms are simply exclusive. This thing about wanting to say "you can do one in the other" is simply an attempt to appease everyone and stop fights. But it is pointless as people who want to do OOP want direct access to objects, and people who want to do FP want direct access to functions, and telling OOP people to go through monads and telling FP people to go through instance methods is not going to cut it, ever.
@@jboss1073
No. It isn't a way to appease everyone or stop fights. I firmly believe that OOP is completely useless in every situation and only serves to bring harm.
Furthermore, the costore comonad doesn't "have to use the monadic interface" in Haskell. Comonads are not monads. They can't use the monadic interface, fullstop. This is an extremely idiotic thing to say and it seems like only someone who hasn't really used Haskell would utter.
Secondly, we already have costore comonads that are very similar to OOP and don't use any sort of monadic interface: lenses.
Thirdly, we have OOP in the form of monads as well which can use the monadic interface. Just because it is monadic doesn't mean it can't be OOP-- once again, that's a very idiotic thing to say. Monads are an extremely generic thing and just about everything can be made a monad. Objects are such thing. Simply because you use Monads along with objects doesn't mean that you don't have OOP. If that was the case, then JavaScript would be FP rather than prototype/object-based. But it isn't. And it is still OOP despite emulating a weak form of monads using promises.
I highly suggest you don't respond and do more research for this rather than talk about things you have no clue about. The things you've just said are so obviously wrong it's laughable. I suggest looking up "lenses are costate comonad coalgebras" to get a grip on exactly how wrong you are. Have a good day.
@@ribosomerocker Luca Cardelli's talk "1996-10 A Theory of Objects (OOPSLA Tutorial)" on slide 45 says:
"The only operations on objects are:
~ method invocation,
~ method update."
Hackage's Comonad page says:
"There are two ways to define a comonad:
I. Provide definitions for extract and extend satisfying these laws:"
Extract is invocation, extend is update.
Hence, objects are comonads.
Comonads are dual of Monads.
So, Comonads do not use the monadic interface in Haskell. I was wrong.
However, dealing with Comonads in Haskell is still similar to dealing with Monads, in such a way that the codo-notation package exists, acknowledging that codo is "analogous to the do-notation for monads.".
Hence, one could correctly say that doing OOP in Haskell utilizes an interface analogous to the monadic interface.
So I was missing the word "analogous" in order to make my phrasing that "Comonads/Objects use the monadic interface" correct.
Not far from the mark, I think.
"Secondly, we already have costore comonads that are very similar to OOP and don't use any sort of monadic interface: lenses. "
Well, the package lens uses Control.Monad.Error.Lens for its errors - so if you want to catch lens errors, you are forced to use the monadic interface. Hence, your statement that "lenses don't use any sort of monadic interface" is incorrect.
"Just because it is monadic doesn't mean it can't be OOP"
Actually, it does mean just that.
OOP is the dual of FP - just like, analogously, comonads are the dual of monads.
So to have "monadic OOP" is just as oxymoronic as having "comonadic FP" - a mathematically impossible proposition.
What you are talking about is "emulating objects monadically". But those aren't mathematical OOP Objects according to Luca Cardelli et al. since mathematical OOP Objects are always comonads and no monad is a comonad (in fact the opposite is almost true, as every coalgebra gives rise to an algebra, which is why you're thinking that OOP can be done within monads).
"Simply because you use Monads along with objects doesn't mean that you don't have OOP."
Yes, it does. Again, if you mix monads with objects, you only have "make-believe objects" but not the real "Luca Cardelli's comonad-equivalent objects" as mathematically defined.
" If that was the case, then JavaScript would be FP rather than prototype/object-based."
JavaScript is not declaring itself based on either algebra or coalgebra.
None of its purported abilities come from a theoretical, mathematical basis.
Hence, JavaScript resists any academic labeling with regards to its paradigm.
Only informally can you assign a paradigmatic label to JavaScript.
JavaScript is indeed "prototype-based" (it changed a bit with classes) but it is not interested in academically defining what that means.
So we shouldn't include JavaScript within this discussion.
"I highly suggest you don't respond and do more research for this rather than talk about things you have no clue about. The things you've just said are so obviously wrong it's laughable."
Too late, I already replied.
And it looks like the things I said were not "so obviously wrong" as you thought before.
You're still coming across as someone who desperately wants to place OOP in the same category of respect and usefulness as FP.
Just like most OOP programmers who are insecure about their choice of paradigm.
The reason I say this is because you're here trying to argue that OOP can be done monadically. But that is a contradiction in terms, to which you're ignorant because you think OOP is just a "vague idea" that can be "shoved" into any algebraic structure.
This is you confusing coalgebras with algebras.
Just because the behavior of coalgebras in comonads can be emulated monadically (every coalgebra gives rise to an algebra so this is not surprising) you therefore think that OOP and FP are compatible. They're not. They're duals of each other. OOP is FP with the diagram arrows inverted. It is literally the opposite. So OOP cannot be done in FP and vice-versa.
Unfortunately for types like you who keep trying to make everything "harmonic" telling people that opposite paradigms can work together.
You're the pseudo-science doctor of programming.
Keep your last answer up to show everyone how little you know.
I recently had to maintain a script written using the functional programming paradigm (in a multi paradigm language) to handle some basic operations. There was a bug occurring somewhere and I wanted to inspect the values in the circuit. Went something like:
"array = (startArray || []) |> dedupe(flatmap(foo(bar(baz))))"
It took me 30 minutes to reason about the control flow. To unwrap the steps in my head, I had to hold a imagined stack of all the steps in reverse. Sure I could run unit tests to probe - but sometimes you just want to glance at code - particularly if you don't have the environment set up and it's a quick fix.
It is certainly concise and clever keeping all of that logic on one line, but it's exhausting unwrapping it to find a bug or change.
So I rewrote it, expanding it into a verbose, boring, procedural control flow - using a Set to deduplicate entries, nested for loops to mimic the flatMap, etc. I added additional logging and safety guards then found the bug.
Though it probably looks offensive and old fashioned to anyone reading it, it's impossible to get lost because all of the behaviours are explicit and self documenting. Regardless of whether you come from a JavaScript or C background - you would understand the procedural control flow. Lastly, when emulating the circuit in your head, you don't need to hold as many steps so it's harder to get lost in thought.
IMO I'm open to FP and I do think it has its merits, but in my experience, it's sort of a meme and not suitable for most use cases.
It is true that FP is harder to comprehend. This is because there are fewer grammatical constructs: just one concept of recursion instead of three different loops, and soon. And our natural languages have lots of grammatical constructs for a reason: our minds need them as guides!
But I still prefer FP because it lets me define new concepts without grammatical quirks. After all, every word can be verbed!
@@mattinykanen4780 I don't think that @DavidAlsh is too dumb to “comprehend” FP, as you insinuate. He points out real problems that happen if you code in the Real World(TM), with stake holders, projects owners, etc. who want you to get the job done in no time. - On a deeper level, the point is that computer architecture is by design imperative, so there will always be a huge gap between FP and the actual code that is generated. And, yes, you have to sometimes inspect that generated code (in the Real World(TM)). This is where being able to debug code in a traditional way becomes essential.
@@8BitInsekt I am sorry about the alleged insinuation; none was intended. I was just excited about the book "The Programmer's Brain" by Felienne Hermans which I had just read.Its neuropychological view of programming, coupled with my own superficial misunderstanding on the principles of both human and programming languages were behind that brain fart of mine.
Funnily enough, just last night I read "Intermediate Science Knowledge Predicts Overconfidence" by Carmen Sanchez and David Dunning (Trends in Cognitive Sciences. 28(4) pp. 284-285, April 2024) - yes, _that_ Dunning.
Your comment came therefore just in time: Without it, I might have remembered that article as "_they_ are overconfident" but thanks to you setting me straight I will remember it as "_I_ was overconfident" instead.
That said, it will still stand my ground on the original issue: there is _no_ completely seamless multi-paradigm programming language. Either the seams show in the language itself or in the mind of the programmer, but they are there. I moreover believe that this mismatch will be inevitable.
Luckily my brand new Boston Scientific Vercise Genus R16 brain diaper will block such secretions from now on.
why didn't you step through it with a debugger to understand it?
instead of focusing on a programming paradigm you should focus on writing good code
If you do FP right, OOP (minus inheritance) should fall out as a special case where you combine reducers to achieve your goal. Technically FP can also fall out of OOP as a special case where the messages and DTOs hold all the data and you overwhelmingly use stateless strategies to describe behaviour, but without lambdas it's ridiculously verbose. C++ had Comparers before they budged and introduced lambdas.
Easily modeling a domain is absolutely one of the strong suits of FP - most FP languages have algebraic data types which make modeling a much more straightforward task than figuring out a complicated object hierarchy, especially in languages which don't support multiple inheritance. Learn about modeling a domain with product and sum types.
Absolutely in love with those transitions where you re-use letters to compose new texts, how did you do that?
Hi I use powerpoint with the morph transition! It's actually very basic 😃
Code Report has a video on how he makes slides like this if your interested. And now with power point using more AI, I am guessing these transitions will get even better.
@@codepersist I honestly think you should have kept that to yourself haha
I like OOP. I don't like to be forced to do it exclusively without free methods (*cough* Java), but I like it.
But Inheritance and choosing that as foundation for polymorphism? Yeah that stuff can burn in hell.
Java has first class functions, which is what i assume you mean by 'free methods'. (a 'method' not tied to an object is a function so a 'free method' makes no sense)
@@adambickford8720 I meant a freestanding function (free function). Yeah, free method isn't really a term.
Java doesn't really have these. Everything has to be a class, so instead of, IDK, defining "pub fn euclidian_distance(start_pos: Position, end_pos: Position): f64" in a file called helper.[programminglanguage] I've got to write "package utility; public class Utility { static public double euclidianDistance(Position startPos; Position endPos) }" - the modern language example also has nicer namespacing by default.
That Java - and C# too - forces you to stick everything in a class is annoying. The only reason I can come up with for it is because they wanted to stick with exclusively dynamic dispatch, but, I mean, Kotlin has freestanding functions on the JVM, just add a bit syntactic sugar if you need your dynamic dispatch because your runtime can't do without.
Kotlin btw is (close to) how to do a proper modern OOP language. Classes are closed by default, methods final, you don't have the awkward differentiation between Wrapper classes and primitives which are the one thing without methods because dynamic dispatch, you have methods to deal with "encapsulation" in corporate - getters and setters - without having to add additional tooling - Lombok - or manually maintaining a shitton of getters and setters that just serve to OCCLUDE the ONE time where that approach was needed, it ACTUALLY HAS A TYPE LEVEL CONCEPT OF NULLABILITY JAVA. Yeah, that last one makes me really mad. (Almost) every method call in Java is a Null exception just waiting to happen.
@@adambickford8720 I'm pretty happy with how Java supports lambdas, but it's not quite as smooth as languages designed with first class functions in mind.
Java has to have it as a hacked on feature where each lambda is a class behind the covers. As I understand it's related to why lambdas can only access variables in the closure that are effectively final. They also had to add all the function interfaces like Function, BiFunction, Supplier, instead of having a type system that actually supports higher order functions.
I use Java daily though and considering the constraints they're under, I think the language designers did a great job adding even this level of support for lambdas.
@@telaferrum Yes, it's an implementation hack similar to 'erasure' in generics. Awfully silly you can't cast the exact same function body literal between 2 different compatible functional interfaces.
And yes, theres a Java tradition of providing the minimal language changes to support a new clunky library api. It took them a decade to give us collection factory methods instead of literals? Optional vs a simple `?.` operator? Seriously, no Tuple?
All that said, they are most certainly first class, higher order functions. Its like watching your grandfather ride a dirt bike; really janky and on the verge of something terrible... but he's doing it so far.
I think OOP just has too many footguns. Inheritance sounds good but get extremely hairy very quickly.
More FP stuff reduces that footgun significantly.
Can also do OOP with more composable structured than inheritance though.
the thing about java i like, the new versions of java provide cool things for example records make u get rid of boilerplates, also stream API which opens the door of functional programming in java
2:49 is a Java thing, not an OOP thing. OOP has boilerplate, sure. But this wasn’t an example of that.
True
Many examples given in this video aren’t meaningful
Also the car maintenance example was a little far from context imo
I am a FP lover and I uses classes mostly just to build FP constructs like custom functors or quasi-monades and stuff I am using TypeScript and NodeJS and I will agree that I would need to be able to help myself with an hybrid mindset it is probably difficult for anyone to be able to balance both paradigms and make use of them efficiently I am also curious about what others paradigms should be on the top of the list… I am not a purist so yeah for me using classes to define functional programming constructs is still functional programming and I then probably would have to think about what I would be calling strictly OOP in a similar manner where I would be using functions to help myself in a FP style but in an overall OOP mindset 😅😅😅😅
Don't forget good ol' procedural also!
Plenty of smaller units of functionality especially can get along just fine with procedural
When I want to know if I understood a paradigm well, I try to use it in C. For example, trying to get right OOP in C can be a very fun challenge.
I like the approach of Erlang/Elixir, which is 'functional' when defining small, precise tasks, and uses the Actor Model when designing the whole system. It builds systems on asynchronous "nanoservices" that send messages and are supervised. This approach embraces the "let it crash" philosophy, allowing processes to fail and restart instead of trying to handle every possible error scenario.
Yes, if you want to get the performance of an old mechanical till out of your desktop supercomputer, then use actors. By all means. ;-)
statically typed functional languages allow you to avoid the pitfall you mention -- here's an example of the car class using OCaml modules:
module Car = struct
type t = { name: string ; miles_driven: integer }
let drive t miles = { t with miles_driven = t.miles_driven + miles }
let needs_maintenance t = t.miles > 100
end
the module system and strong typing allows you to keep data together and act on data like you would with a class while maintaining immutability and functional guarantees. the problem with python is that you don't have a good way to group multiple bits of data together without using a class (maybe named tuples?)x
good video! i think learning to use multiple paradigms expands the repertoire of tools you can use to solve a problem and combining them can truly form some good code. it is probably necessary to talk about dynamic vs. static typing when mixing paradigms though, and striking a balance could be difficult. i like your use of python as an example, as it is illustrative of a good and balanced multi-paradigm approach.
It comes down to the requirements of the problem at hand and knowing which tool is the appropriate tool to use. It is also important to know when and where to use said tool and how to properly implement it within said language. This is applicable from Op or Machine Codes to Assembly all the way up the chain of abstraction to even Typescript. There are many paradigms out there and many different ways to solve the same problem and every approach will have pros and cons. The target of priorities ought to be Efficiency and Performance, Readability, Reusability, Reliability, and Easement of Maintainability and not necessarily in that order. The order may vary based on the specifications of the requirements of that job, task, feature, program, application, system, network, etc...
Other than that, great video and keep up the good work!
Rusts’ healthy combination of both OOP and functional styles are one of the many reasons I feel it’s the best language we have to date
Literally almost every modern language has a healthy mix of both OOP and FP
@@Vexxel256 I also think that every modern language has a healthy mix. But the way Rust does implement it is way beyond what I thought a programming language was capable of. Just beautiful
Yeah, until their new policy comes into action and kills the community with law suits.
@@fabricehategekimana5350 Rust doesn't even have anything OOP-related.
@@jboss1073 it depend of the definition of OOP. According to wikipedia (en.wikipedia.org/wiki/Object-oriented_programming) OOP bring together data structures (generally product types) and function (named methods).
Rust gives a way to perform the same result with associated functions (according to the uniform function call), impl and traits (one of the mechanisms that promote composition over inheritance en.wikipedia.org/wiki/Composition_over_inheritance#Avoiding_drawbacks).
We also have abstraction, encapsulation (private members) and polymorphism (generics). Rust only support behavioral (and not structural) inheritance via traits and trait dependence. So it's true that Rust is not classic OOP but an OOP-dev can still work with it (using design patterns for instances)
I sorry I never started my oop projects in C#, I wasted so much time trying others languages.
Code Report inspiring the new-gen of programming youtubers, nice vid :)
"[Functional Programming] can be considerably harder to scale up". I've found the opposite to be true after almost 30 years of coding, but you do have to switch mental models and use different code organization techniques.
The car example could look nearly identical in FP as in your OOP example. Just replace "public void drive()" with "public static Car drive(Car car)" (assuming Java)
Though mutability is cool when managed well, so I would do "public static void drive(Car car)"
I write rust and pride myself on writing immutable functional code. That being said, I still often talk about the larger parts of code with my team as being Classes, and having Methods, and Fields
Why not just use the word "module" then? Isn't that what you mean? (IDK if that's a different thing in Rust or not)
@@ConcerninglyWiseAlligator when I talk about classes in rust, I mainly mean a longer lived struct with a constructor, state, and associated methods. nothing stopping you from having 2+ of these in a single module
I like your style of making videos. Keep it up. This is really good for a second video.
You just saved my 2 days' worth of college classes. Thank you.
im extremely artistic swap the r for a u and I tend to hyper over engineer. OOP helped facilitate that tendency and as a result i never completed anything. took me many years to ditch a lot of OOP nonsense and incorporate functional and procedural aspects. Once I did that I actually started finishing projects.
Nice video, thank you for making it.
I learned a lot from it and I enjoyed watching it.
defacto, if you program well you tend to use 3 basic paradigms: structured, object-oriented and functional programming.
I recommend reading clean architecture to find out if there really are paradigms. It's better not to mislead people
Your problem is you are still thinking in OOP terms while using FP. Why on earth would you find if car needs maintenance after the fact. As soon as car has driven more than the stipulated amount of time, it would go for maintenance. The flow would retain this information until it is used. If you keep thinking on OOP way, obviously FP will not make sense and it will happen often.
Man, I'm happy I subscribed to you!
Check out Scala as an example that combines OOO and FP really well, much much more elegant than Python.
0:38 Not all OOP is class-based, for example JavaScript uses prototypes instead
2:00 That's a really bad example, "private" isn't for personal data you don't want others to know, it's for any field you don't want users of your class to modify without telling you (which is usually all of your fields, especially if you consider that changing a field's visibility after the fact will break a lot of existing code). You provide a getter for fields that users of your class should be able to read, and a setter for the ones they should be able to write, which lets you check that the new value is valid and adjust other fields accordingly to make sure the object stays in a consistent state. With a public field you can't prevent someone from going `obj.name = null;` and you probably don't want that to happen.
About Java's boilerplate, you can do 'void main() {print("Hi");}' in the latest preview, I think
Now's too late, they should have done that before Python was born!
Structured imperative is the most fun to use.
Well, at the very least BrainF is the most fun to use. I don't know about other languages.
Your explaination skill is amazing.
I love all paradigms of Coding, C# for OOP (my favourite programming language), C for procedural/imperative (because i kind of a masochist myself) and JS for FP (job related), i know there are more then these paradigms but i can tell you for sure that Prompt Paradigm (thanks to ai) will become a thing and i already hate it :(
Yeah, people that keep saying to use this or that paradigms exclusively completely ignore real world applicability.
1:51 this example is really bad for educational purposes regarding encapsulation, because you are making it seem like making fields private makes them "secure" from anyone reading them, which is not the case because everything resides in memory and can be read easily.
you imply "sensitive data" should be in private fields, but this is fundamentally NOT what the private field is for.
It is for giving the ability to modify/access a field exclusively to the class itself and nothing outside of it. A better word than "sensitive data" would be "internal data" that you dont want anyone outside messing with, and if they can it would only be through your defined methods like getters/setters.
Polymorphism is the only reason I would use inheritance. As far as i know, it was one of the major reasons inheritance was created.
Loved this video !!!
Objects are beneficial to use when they can actually represent something real. Lets say a sprite in a 2D game. You store it`s data, have it`s properties like x,y location, methods like draw and update position, but everything else doesnt need to be an object and can be easly reprepsented with FP: get inputs, update objects positions, draw objects, set outputs, repeat.
And it`s easier to understand the original idea behind OOP when you go through the history of programming languages, how they evolved and what OOP tried to solve. It was just an extension of structs and structs represented properties of some 'stuff'.
If you are programming games in OOP then your performance will be abysmal. The original idea behind OOP was to tell the compiler how to reuse code. It had nothing to do with structs.
@@lepidoptera9337 functions were designed to reuse code, not OOP
@@karolstopinski8350 Functions allow humans to reuse code, but they don't tell the compiler that it can substitute one piece of code for another. Inheritance is meant to tell the compiler to automatically reduce the code used by a derived class to the code in the base class if that's sufficient. The idea is flawed, however, because it leads to forking in cases where the method of the base class is not identical to the method of the derived class. That's one of the reasons why debugging OOP is such a nightmare.
Paradigm doesn't really matter, but the patterns you achieve with them do.
3:26 There is no side effect. `global_var` on line 3 is actually not global, it's local to `add` function call. That's because of assignment on line 3. It shadows `global_var` from line 1.
You can write `global global_var` or `nonlocal global_var` between lines 2 and 3. This way assignment on the last line won't create a new local variable, but will use `global_var` from outer scope.
OOP is less about classes and polymorphism and more about messages and encapsulation.
OOP was never about messages. The original use of OOP was to reduce memory footprint with automated code reuse. Then Kay entered the scene and was completely misunderstood by people who never listened to him. :-)
No need to pass a lambda to sorted at 3:06. You could have simply written it as: sorted(names_array, key=len)
I'm going to be quite harsh, but honestly, this sounds more like a beginner's view of OOP and FP. At best incomplete, at worst completely wrong.
private/public defines the scope in which the variables are accessible, it's not related at all to privacy or security. It would be completely normal to have the SSN be a public variable in a program that deals with SSN.
Polymorphism also isn't an OOP thing. It was first brought on by FP and was added to OOP later on. There are also many more types of polymorphism than the simple subtype polymorphism you mentioned. For example, parametric polymorphism, which is the main type of polymorphism used in FP
I also don't think talking about FP without having any experience using a FP language is a good idea. I am afraid that it is misleading more than helpful
Procedural >>>>
This is making me feel like sooner or later someone will have the bizarre idea of just coding in a way that makes the code works and not worry about a specific constraining structure to adhere too.
That's basically imperative programming though...
FP presented here is just the functional style you can follow in many imperative languages.
But, you simply can't reliably do purely functional programming if your language isn't designed for it. Similarly, to follow the logical programming style, you need a logic programming language.
Functional programming is notoriously hard to define. Purely functional programming is way more clear-cut tho. Here is what defines it:
0) time and execution don't exist. there's only evaluation
1) basically everything is an expression
You will usually pair that with:
2) each valid expression has a precise type
3) data types are expressions too and so you get polymorphism (generics, interfaces, etc.)
(0) makes the code dead simple. The only complexity you get is what you write or import.
Because of (1), everything composes. This removes boilerplate and allows for limitless abstractions.
(2) limits that composing to reasonable and meaningful code. It let's you eliminate invalid states, write code that covers all cases and avoid defensive programming.
(3) let's you encode invariants and other important properties within your abstractions. And in some languages it even allows you to write proofs about your programs.
Funnily enough, when using a purely functional language, you can usually write certain parts in the imperative style. Like Haskell's State Threads or IO and State data types. Heck, it could probably model objects too! But I don't think anyone has bothered.
So yes. Pick the best paradigm|tooling for the job, but also do explore your choices!
> Similarly, to follow the logical programming style, you need a logic programming language.
Kanren begs to differ. It was ported to more languages than there are people using it.
@@vitalyl1327 Nice to learn something. Thanks!
Tho I'd argue DSLs are kinda languages on their own. But yeah, the line can get very blurry
@@mskiptr sure, when you have a proper meta-language, you can turn it into any language imaginable. Beauty of Kanren though is that it is built on the very basic conatructs that are present in pretty much any language, and yet they're sufficient to do logic programming. Of course, harder to do so with, say, lazy FP. Just interesting how not really alien the logic programming is, despite its alien appearance.
Stop thinking about programming in paradigms, just make fast/safe code no matter the implementation.
3:18 Shared State, Mutable Data, Side effects
well... either "Shared State" or "Side effects" is redundant here - one the remaining two words suffice .... i think
Programing paradigms are Tools. One is a hammer, another is a plier and another is a screw driver. Thinks go wrong when you are limited to one Tools and try to hammer a screw.
literally just into 2nd module of MS Introduction to Python tutorial covering 'Introduction to object-oriented programming with Python ' so super noob at coding but already see the strengths of OOP as a paradigm but the OCD in me looks at Functional and goes 'so pretty'
thanks so much for the vid - really added to what I am covering at the moment with OOP also.
You explained it very well. I just don't know for WHOM the explanation is. A beginner can't make heads and toes from the acronyms. Let alone understand the complexity of the problem itself. An experienced programmer is mostly defined by the language he uses (or is 'forced' to use). Maybe a reminder to think about the whole thematic? Well done, either way.👍👍👍
on 5:00 i usualy use json as data for that situation. it easier to read than only the value
When I was a kid. Which is few months ago
I thought "Python is the best programming language and functional programming is the best way of programming."
Which paradigm use, depends on task what you should solve !!!
I agreed to use Erlang, isn’t that enough?
"Sideeffects are something developers avoid"
Meanwhile C libearies: provide your own buffer for the data to be stored
How do you make youtube videos like these? These animations are so clean and beautiful yet simple. Is it just video editing or are there other techniques involved.
U can use strict to model in FP and u don't model your problem u model the data and can use something like a changeset to set when the car driven is over 100 house need to do maintenance
Personally, I think its better to start with functional programming at least the style of it, so that you can understand the action part of the programming before understanding the structure part of the programming that OOP focuses more on
What did you use to display the code? It looks really good. Also really great video.
Embrace no paradigm: all hail the mighty Nim!
I will use elixir for the car exemple,
defmodule Car do
defstruct maintenance: false, hours_driven: 0
def drive(%{hours_driven: hours} = car) when hours > 100, do: %{car | maintenance: true}
def drive(car), do: car
end
Yes, we don't need if in elixir but if you want:
def drive(car) do
if car.hours_driven > 100, do: Map.update(car, :maintenance, true), else: car
end
oops most important feature supposed to be messaging and not encapsulation
Nice video. 😀 Btw, can you please tell which software are you using for those transitions between two code blocks? Looks smooth.
Yeah! Its actually just powerpoint with the morph transition for the text!
@@codepersist Nice. Guessed it but was wondering. Good work.
Hello and thanks for providing high quality data.
could I ask you, what is the tools which you have used to create this greats animations?
C# isn't a multi paradigm programming language. It is strictly an object oriented programming language. Even for a pure function, you will have to encase it inside a static object
meanwhile I use both for the same project because I'm the only one working on it
Lower the music volume
can you make video about code forces and competitive programming