4 ms·
https://news.ycombinator.com/item?id=24358711 https://news.ycombinator.com/item?id=24358711 OOP only provides illusions of reuse-ability. In reality every comp
by leafboi 6y ago
https://news.ycombinator.com/item?id=24358711 https://news.ycombinator.com/item?id=24358711
OOP only provides illusions of reuse-ability. In reality every component created in OOP takes extra steps and extra glue code to make compose-able.
In programming you want to build small primitives that you can compose to form larger higher order primitives. This allows you to reuse lower level components to form new programs. OOP does not allow you to do this easily.
- wolco 6y agoToo many small primitives lead to grouping. An object can be a collection of primitives.
- leafboi 6y agoAnd isn't a collection of primitives a grouping in itself? An object is a grouping of primitives. So according to your logic OOP should have the same problem. The real issue isn't grouping. The true nature of abstractions is the formation of higher level abstractions from "groupings" of lower level primitives. So "grouping" is fundamental to abstraction and therefore fundamental to programming. The problem with OOP is that the groupings are forced. I cannot reuse a method outside of the context of the object because the method is tied to mutable state. For example: Try to write a function addOneTimesTwo from the composition of the three primitives below (in python): def addOne(number): return number + 1 def timesTwo(number): return number * 2 def compose(f, g): return lambda x: f(g(x)) Answer: addOneTimesTwo = compose(timesTwo, addOne) You will note, compose is a universal functional operator. The definition for compose above is the most primitive way to compose functions in FP. Try to do the same with OOP: class AdditionObject: def __init__(self, number): self.number = number def addOne(self): self.number += 1 class MultiplicationObject: def __init__(self, a, b): self.a = a self.b = b def mult(self): return self.a * self.b Certainly do-able with OOP. But I cannot extract the methods away from the state. In order to use the banana (method) I need the gorilla holding it and the entire forest it lives in (Object) to come with it. It can be done, but generally you need a lot more glue code to make it work especially given the fact that people can get more creative with OOP. As you can see the MultiplicationObject is an example of this creativity. It was written with a different interface then the AdditionObject. Basically if you know haskell even similar issues to the above can be dealt with in terms of haskell. You can certainly define a composition operator for even the most complex types. The issue with OOP is that EVERYTHING is a complex type. A standard Object already has enough complexity to necessitate custom composition operators so you're basically writing custom glue code everywhere. Either you're writing complex glue code or your bending all your code to fit into a framework. Nothing is truly modular.
- wolco 6y agoclass Math { public static function addOne (int $i){ return $i+1; } public static function multiTwo function (int $i){ return $i*2; } } With classes those functions or methods are grouped together under a math class. Calling it is as simple as: Math::addOne(2) When you need Math you include it. Seem cleaner than including little addOne functions as global closures.
- leafboi 6y agoLet's say bob wrote One class and Jane wrote another class and these classes are used in many places around the code base. I'm the programmer that has to compose these classes... You're telling me that I have to change the implementation/interface of what they wrote? What about the dependencies? This is what I mean by "Not reusable." Additionally typical OOP involves mutating state. The purpose of the class is to hold methods that operate on internal state. Otherwise what you're writing is basically function calls under a namespace. >Seem cleaner than including little addOne functions as global closures. Again Put it under a namespace called Math; problem solved. The purpose of a namespace is to group functions together. The purpose of a class is to group methods together that operate on shared state. If your methods don't involve a "this" function addOne(){ this.value + 1; } then you're not really doing OOP. You're just repurposing the class as a namespace.
- wolco 6y agoThe number + 1 or x 1 doesn't require an object or oop. It doesn't require a function either and would be better to just inline. The great thing about repurposing a class as a namespace is I can pass that repurposed namespace package anywhere. You never need to change their code either inject those objects into a new class or extend one and inject the other.
- leafboi 6y ago>The great thing about repurposing a class as a namespace is I can pass that repurposed namespace package anywhere. namespaces can be passed anywhere anyway. >The number + 1 or x 1 doesn't require an object or oop. It doesn't require a function either and would be better to just inline. + 1 and x 1 are just examples. If you take the examples and make it more complex the problem still remains. An inlined expression is still a function. It's just not named. y = function(x){return x+1; b1 = 1 + y(2) x = 2 b2 = 1 + (x + 1) In the above code y(2) and x = 2; x+1; are equivalent. >You never need to change their code either inject those objects into a new class or extend one and inject the other. You rarely ever need to do this in FP, but you need to do this all the time in OOP.