5 ms·
You have simply contrived a poor design, then pointed out its flaws. Not only that, you are _constraining_ the solution space (no static methods and must be par
by kingdomcome50 7y ago
You have simply contrived a poor design, then pointed out its flaws. Not only that, you are _constraining_ the solution space (no static methods and must be parameter-less). That sounds like a straw-man to me. There is nothing inherent in OOP that mandates a developer choose your design.
Furthermore, you are comparing a perfectly factored functional design to a poorly factored OOP design. I can just as easily assert that `sort(people)` cannot be factored into `sort(comparable)` because "clearly" the body of `sort(people)` is dependent on some property of `people` that is not present on `comparable`. Contravariance cannot just be assumed.
Said another way, every function is tied to the entire public surface of the data to which it is provided (at least in theory). So unless `people` just happened to have exactly the same interface as `comparable` already, a new, more generic, `sort(comparable)` function would be necessary.
The solutions are roughly equivalent in either paradigm: move the behavior into a more generic container. Where composition can be used instead of inheritance in tandem with double dispatch, for OOP, if the public surface of SortedGroupOfCats must include a `sort()` function.
- crimsonalucard 7y ago>You have simply contrived a poor design I contrived a poor design that is a reflection of what can happen. There is no design theory making all designs perfect therefore many isomorphic mistakes in my design happen all the time uncontrived. >Not only that, you are _constraining_ the solution space (no static methods and must be parameter-less). I constrain it to prevent you from creating a function instead of a method. A function is outside of OOP a method is 100% OOP. a static class without state is equivalent to a namespace holding a bunch of functions. If I didn't constrain you you'd be proving my point by switching to using a regular function instead of OOP. >There is nothing inherent in OOP that mandates a developer choose your design. That is the problem. There is something inherent in functional programming that mandates the developer to never be able to make the previous design choice. If all your functions are stateless there is NO way you can even contrive the poor design I contrived earlier. That is the secret. Less expressivity is less ways to screw up. Imagine a programming language where the syntax only allows you to design your programs in the best possible way... Functional programming is closer to this goal than OOP. >I can just as easily assert that `sort(people)` cannot be factored into `sort(comparable)` It's better to factor something to be more general than to write 10 flavors of the same thing. How hard it is doesn't matter here, rewriting sort to solve this problem is better than OOP where you have to write another version. That is all I am arguing for. >Contravariance cannot just be assumed. The entire essence of my argument is that you discover this property in two random places, but you cannot apply a function in both places because the function is tied to another context. >a new, more generic, `sort(comparable)` And this is better than two specific flavors of sort. Let's scale this up to an app that's been around for 5 years.... in this case One generic sort is better than 20 different flavors of sort. >The solutions are roughly equivalent in either paradigm: move the behavior into a more generic container. But like I've been saying you can't just move the method in OOP you have to change the object hierarchy. If the method is to be used in another unrelated object hierarchy than the hierarchies have to be merged via multiple inheritance. All I want is the function, why am I bringing the whole object ancestral structure with it? "I think the lack of reusability comes in object-oriented languages, not functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle." -joe armstrong
- kingdomcome50 7y ago>I can just as easily assert that `sort(people)` cannot be factored into `sort(comparable)` You are misunderstanding what I am asserting here. If the body of `sort(people)` is coupled to a property of `people` not present on `comparable`, you cannot _just_ refactor to a more generic `sort(comparable)` and expect the behavior to remain consistent. The solution requires two functions: `sort(comparable)` that abstracts the portion of the function body concerned with the surface of `comparable`, AND `sort(people)` that interacts with the property of `people` not present on `comparable`. It's obviously worth noting here that `sort` could just as easily be some other verb, say `validate` (I understand `sort` doesn't make much sense in this particular context). Similarly, there is no need to "move the method" or "change the object hierarchy". You conveniently failed to quote the one line in my response above that addresses this... >composition can be used instead of inheritance in tandem with double dispatch, for OOP, if the public surface of SortedGroupOfCats must include a `sort()` function And while it makes for a great quote, Joe Armstrong is simply wrong. There is nothing inherent to OOP (and even less so when examining the languages that utilize the paradigm) that requires poor design. What if I wanted "the banana that one specific gorilla is holding in some particular jungle"? How would that look in OOP vs functional? More importantly, is there a meaningful difference or is it just semantics? FWIW, I prefer a functional paradigm myself.
- crimsonalucard 7y ago>You are misunderstanding what I am asserting here. If the body of `sort(people)` is coupled to a property of `people` not present on `comparable`, you cannot _just_ refactor to a more generic `sort(comparable)` and expect the behavior to remain consistent. Refactor sort into the composition of two functions. One is reliant on people, the other is just sort. The signature of the original sort function remains the same. But you have a general function that can be reused in different contexts. This type of change CANNOT be done on OOP. >composition can be used instead of inheritance in tandem with double dispatch, for OOP, if the public surface of SortedGroupOfCats must include a `sort()` function composition moves the entire state across. Think about it. I have SortedGroupOfCats and you want me in order to reuse sort, move SortedGroupOfPeople into SortedGroupOfCats. >And while it makes for a great quote, Joe Armstrong is simply wrong. There is nothing inherent to OOP (and even less so when examining the languages that utilize the paradigm) that requires poor design. OOP is bad because it makes it possible to shoot yourself in the foot, and while avoidable, unfortunately the syntax promotes designs that tend to be bad. >More importantly, is there a meaningful difference or is it just semantics? There is.. a function does not need to be instantiated to be used. A function never modified an entity. That changes everything. The fact that lambda calculus and turing machine exist in a theoretical sense shows you that the industry knows about an intrinsic difference between the two concepts. >FWIW, I prefer a functional paradigm myself. Note that although I have used the functional paradigm as an example this is not my overall critique. I am not just saying OOP is better than functional programming. I am saying OOP is worse than imperative, procedural and functional programming. Out of all of the most popular ways of programming... OOP is the least modular and the one with many many design issues. Even non-functional programmers have noticed this... GO is one of the results of this realization. There are cases where OOP works, but in most cases, it's sub-optimal.