Hacker News new | past | comments | ask | show | jobs | submit login

> The function would not be tied to state and that function can be executed in the context of anything.

Non-OOP functions are still written against the interface of their parameters, so they remain tied to state.




Interfaces are usually stateless as they only contain methods.

Technically, the function body itself is a form of state but I addressed this technicality above.


No, I mean the function (e.g. a C function) assumes a particular type of parameter. So it's bound to that type of parameter (by accessing its fields) in the same way a method is bound to its object. It's the same problem.


Instructions acting on types or interfaces in the purest sense are just actions and verbs... it is not state. The technicality arises when you consider the fact that for the instructions to even exist you must write the instructions down... the result of writing something down is the creation of state.

So to address what I think will encompass what you're referring to and a lot more than that... anything that is written down as code is in itself state, including types, interfaces and the function body. But as I mentioned before this is a technicality.

We pretend that the computer is executing functions when it is actually executing assembly code that imitates functions just like we how we can pretend functions are stateless even though their very existence implies state.


You can think of the computer representation of functions as a leaky abstraction as the statefulness of reality leaks into the virtual world.

There are languages that take advantage of this leakage by allowing you to pass functions around as parameters or aka first class functions. I'm sure you've seen this and used it as it's pretty common now.


I think the argument is that the difference between `noun.verb(state)` and `verb(noun, state)` is only semantic. The former is just syntactic sugar for the latter under the hood (or not. Look at python). The dependencies are the same.


I've seen your argument b4. I'm not sure if he's referring to that.

Either way it's more than semantics. Let's say I have two nouns. Noun1 and Noun2. Only Noun1 contains the verb method. Noun2 does not. How can I use noun1.verb on noun2?

I cannot use it. But I can use verb(noun, state) on either object. noun1.verb forms an arbitrary wall that becomes technical debt. Even modifying noun1.verb will not help, the only way is to move it AND modify it and change all the things that are dependent on it...

While verb(noun, ...) at most requires a modification, that's it. Semantically the "meanings" are the same but from a modularity perspective there is a difference in the "agility" of the method.

Let me put it simply. a method is tied to its parent object/interface by type AND scope of the parent instantiation. You cannot reuse the function outside of the scope.

A function is only tied to the type. Your argument is true if you're only thinking about types.

You might be thinking that the "type" of noun is really the same concept as scope and that if you get the type of the "noun" right then both examples are the same. You're not wrong in this sense but rarely do people place the right method on exactly the right interface or object. Usually they place it on an interface and realize (months or years later) that the method can be reused on 30 other unrelated objects. The change to correct the design on such a discovery involves a massive rewrite of a type hierarchy. It is a huge effort and often not undertaken.


I find it _much_ more likely that shoehorning `verb(noun1, ...)` to accept `noun2` will result in sub-optimal design than factoring out an interface (`verb(...)`) that both `noun1` and `noun2` will implement. A verb's meaning/implementation can shift dramatically depending on the noun to which it is attributed. E.g. `validate(user)` is materially different than `validate(order)`.

Any argument predicating on "massive rewrites" and "huge efforts" is a straw-man. A programmer can model dependencies incorrectly using either approach above. Expanding a discriminated union (to include `noun2`) will lead to the exact same amount of refactoring as extracting an interface. We know this because it _is_ a matter of semantics.

The discussion at hand is about comparing/contrasting the dependencies between object-methods (`noun.verb(...)`) and stand-alone functions (`verb(noun, ...)`). From the POV of the method/function, the dependencies are the same. Whether `noun` is explicitly passed as an argument or compiled into `this` does not change what information is available to the function body (i.e. it's dependencies).


>I find it _much_ more likely that shoehorning `verb(noun1, ...)` to accept `noun2` will result in sub-optimal design than factoring out an interface (`verb(...)`) that both `noun1` and `noun2` will implement.

I am not talking about the case you describe above. Get rid of Union types, that is a whole different thing. I am talking about a function that does not need to be shoe horned that can obviously be used for members of both noun1 and noun2 but due to a design decision the function is tied to noun1.

Let me give you a concrete example that is trivial. The example is so trivial that it probably won't happen in the real world but you can see how for concepts that are more complex it can happen all the time.

Imagine I have an app with a class in it called SortedListOfPeople. It would make sense to put a sort method on this class. Now imagine that this app runs for a bit then you add a feature which involves a class called GroupOfCats. The feature is very far away from and completely different from SortedListOfPeople. Then one day you get the task to turn GroupOfCats into SortedGroupOfCats.

Suddenly you got a problem. How do you re-use the code from sorted list of people without the sorted list of people? How do you use it with a different context at all? Not being able to reuse code is NOT a semantic problem. It is an intrinsic design problem.

Your genius solution above (Note how I call your solution genius instead of straw-man) is two implementations of the same concept. This is obviously more genius than one function.

Sortable types is an obvious example, but think about how often this problem occurs in OOP for more complicated concepts. You end up implementing things multiple times all over the place.

If the sort function was a stateless function operating on a sortable interface parameter you would not have this problem. The only thing you need to implement for GroupOfCats is to give it a sortable interface rather than reimplement sort for everything that needs to be sorted.

Actually if you think really hard about OOP and the sort function. How would you make a generic sort function that can be reused across all contexts while following the rules of OOP? You cannot use static classes and the function cannot take parameters... as that would imply a stateless function. The sort function must operate on the classes own state. How can you then share the sort method across two or more classes in OOP?

The answer is inheritance. Put the sort method in the base class. How would you share the sort function with classes that already have ancestors? Multiple inheritance. Too bad everybody hates these two concepts so they resort to garbage syntax that basically turns the class into a namespace so they can start using functions as if they weren't tied to some intrinsic state.


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.


>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


>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.


>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.


>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.

This is where you lose me. The above is simply untrue. There is absolutely no reason one cannot employ `SortingAlogrithm.sort` within the body of `people.sort()`. Are you suggesting every utility class (and by extension the entire standard lib) needs to be injected into an object before it can be used?

Again, you are creating a false-equivalence. A "fair" example would require our generic `sort(comparable)` be passed into the original as a dependency like so: `sort(people, csort)`. It's _possible_ that the two functions could be composed, but that is completely dependent on the semantics of each function (we cannot just assume composition is possible for every use-case).

The difference between a first-class function and an object is purely semantics. Yes, you have to `new` an object before you can call a method, but there is no requirement any "implicit context" has to be provided at the time of instantiation. Closures offer the exact same capability via "capturing" and partial application.

Conceptually, all OOP does is take 10 functions that all have the same parameter and "partially apply" each of them over that parameter into new functions that don't have a parameter (i.e. they get that "implicit context"). That is, OOP saves you a step. It is common practice in FP to start with "pure" functions then, higher up in the application, partially apply those functions into new, domain-specific functions with your data (a.k.a. dependency injection) that are strung together to carryout a use case. OOP allows you to _define_ partially applied functions instead of the need to compose them (of course this also makes the DI mechanism a bit different as well).

I can agree that the above _can_ make decomposition more difficult, but your general critique reads more of one leveled at programmers not understanding the basic principals of software design, not OOP. I could just as easily create a mess in FP as in OOP.

>More importantly, is there a meaningful difference or is it just semantics?

I urge you to sit down and write out the pseudo-code. It will be plainly obvious that there is no meaningful difference between each approach.


>This is where you lose me. The above is simply untrue.

No. My statements are true. But you are right about you being completely lost.

>There is absolutely no reason one cannot employ `SortingAlogrithm.sort` within the body of `people.sort()`

Let's talk about objectives and what you're implying with this suggestion. The objective is to sort a bunch of people within the the people class.

What is SortingAlogrithm.sort? Is it an object that sorts its own state or is it a stateless class with a stateless function? If it's an object that sorts its own state then it CAN'T do what I want. I want it to sort peoples' state NOT it's own state. If it's a stateless class with a stateless method then you are no longer doing OOP you have created a function that is not tied to state thereby proving my point that stateless functions are more modular.

>Are you suggesting every utility class (and by extension the entire standard lib) needs to be injected into an object before it can be used?

Yes this is the model in pure OOP languages. Otherwise you are using a hybrid of OOP and functions thereby providing evidence that OOP alone is broken. Things like JAVA, C++ do all kinds of BS like this further ingratiating my point.

>Again, you are creating a false-equivalence.

No. You are failing to understand. 'Lost' was the term you, yourself used.

>A "fair" example would require our generic `sort(comparable)` be passed into the original as a dependency like so: `sort(people, csort)`.

This is not fair at all. in fact it's ugly. I pass a generic sort as a parameter into a specific sort? If you think that's fair and you code this way then you're in for a surprise when building a complex system.

>It's _possible_ that the two functions could be composed, but that is completely dependent on the semantics of each function (we cannot just assume composition is possible for every use-case).

I'm thinking you got confused by what I mean by composition. There are two compositions I am referring to in my statements. Function Composition and Object Composition. Object Composition is exactly the previous example you gave sort(people, csort) or inserting something via the constructor People(csort), there's a number of ways to do it.

Function composition of A(x) and B(x) is F(x) = A(B(x)). Function composition is at the heart of functional programming.

>It's _possible_ that the two functions could be composed, but that is completely dependent on the semantics of each function (we cannot just assume composition is possible for every use-case).

If you're not changing state and something has properties of being sortable it can be done.

you have x = sortableListOfPeople

composedFunctions = f(sortableListOfPeople) {return doPeopleSpecificStuff(sortGeneric(sortableListOfPeople))}

you can go the other direction if you want. The implementation is situation specific but if something is sortable this can be done always but only if all functions are stateless and don't modify values. Note composedFunctions should have the exact function signature of the original function thereby negating the need to change other things dependent on it.

>The difference between a first-class function and an object is purely semantics.

No. A first-class function cannot change. It is immuteable and when called with the same input Always has the same output. An object with a method when called at different times can have different output based off of internal state. Your statement is categorically wrong.

>Yes, you have to `new` an object before you can call a method, but there is no requirement any "implicit context" has to be provided at the time of instantiation.

Then you are basically declaring a stateless function. The admission that you have to do this is an intrinsic flaw. Yes nothing prevents you from going outside of the OOP paradigm when programming in an OOP language. You likely have no choice due to inherit flaws in OOP. Try doing OOP without this ability... You'll see how broken the whole model is.

>Conceptually, all OOP does is take 10 functions that all have the same parameter and "partially apply" each of them over that parameter into new functions that don't have a parameter (i.e. they get that "implicit context"). That is, OOP saves you a step. It is common practice in FP to start with "pure" functions then, higher up in the application, partially apply those functions into new, domain-specific functions with your data (a.k.a. dependency injection) that are strung together to carryout a use case. OOP allows you to _define_ partially applied functions instead of the need to compose them (of course this also makes the DI mechanism a bit different as well).

Let me tell you definitively. I completely understand what you're talking about, I understand it now and I understood it before. No need to reiterate. What you're not understanding is that despite my 100% comprehension of your statements I am telling you, that you are failing to understand ME.

Let me try to put it as simply as possible. If there is an Object called Number and a method called addOneAndReturn... if I call that method on a Zero Twice? what happens? I get One and then I get a two. The method is returning two different values despite being called in the same way TWICE. This sort of thing is NOT POSSIBLE in a stateless function. It Cannot Happen period. THIS is the fundamental difference. THIS is MORE than semantics.

Yes I understand that currying and first class functions have parallels to Dependency Injection BUT there is still MORE then a semantic difference between OOP and functions.

>your general critique reads more of one leveled at programmers not understanding the basic principals of software design

Design is the domain of things we don't understand. There is no design in type theory or number theory because we understand all the implications of the axioms and theorems. We go to design when there is no formal theory that can say why one design is better than others.

I am not coming from the design perspective. I am coming from the theory perspective. When you tie functions with state the state must move along with the function. If you keep state and functions separate then they can move separately. This is logic. No design principles needed.

Any language with too many design patterns is a language that is very much lacking in a formal description (think: JAVA)

>I could just as easily create a mess in FP as in OOP.

You can. But there are more ways to do it in OOP then in FP. In fact there are more ways to shoot yourself in the foot in OOP than in procedural programming in general. I'm not even referring soley to FP. Even C is better than C++.

>I urge you to sit down and write out the pseudo-code. It will be plainly obvious that there is no meaningful difference between each approach.

You don't need to write psuedo code. Use your brain dude. A stateless function versus a function that carries it's own mute-able state. There is MORE than a semantic difference. The semantic difference you're referring to is when the SELF value is immuteable. In that case the two ways of calling the function become more similar, but THAT is NOT OOP.

There is one rule that needs to be followed to make ANY program FP. Just one rule, that's it. Make all things immutable. OOP is fundamentally different from this because it involves the mutation of Object state.


I fear the goalposts have moved from "the problem with OOP" to "the problem with mutability". These are distinct concepts. The fundamental premise of OOP is, among other things, the encapsulation of behavior _with_ data (as opposed to _around_ data). It has nothing to do with mutability. One can absolutely practice OOP with immutability (looking at Scala here). Similarly, OOP doesn't need to be written imperatively. Are you suggesting `sorted_people = people.sort()` isn't OOP?

You are building your own concept of OOP to argue against, not the one that exists in reality. (straw-man emoji)


>I fear the goalposts have moved from "the problem with OOP" to "the problem with mutability". These are distinct concepts.

No they ARE not. For OOP to work you have to have objects be mute-able. The modern definition of OOP are nodes that communicate with each other and change each others state.

The minute you get rid of mute-ability is the minute you are doing functional programming.

>One can absolutely practice OOP with immutability (looking at Scala here).

Scala is a multi-paradigm language. Also scala is not always immutable. The objects in scala can mutate.

>Similarly, OOP doesn't need to be written imperatively.

It does if you want the properties of an object to change. That is the dichotomy of OOP and FP otherwise there is no dichotomy and no point for there to exist vocabulary to separate the two concepts.

>Are you suggesting `sorted_people = people.sort()` isn't OOP?

If I remove the engine from my car and roll it down a hill while in the drivers seat am I still driving the car? Yes. Technically I am. But key feature of the engine moving the car is missing. You can call people.sort() OOP you can also call it not OOP because it is missing the key feature of self modification.

>You are building your own concept of OOP to argue against, not the one that exists in reality.

lol lets look at the definition. https://en.wikipedia.org/wiki/Object-oriented_programming

I directly quote from the site above:

"Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data, in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods). A feature of objects is an object's procedures that can access and often modify the data fields of the object with which they are associated (objects have a notion of "this" or "self"). In OOP, computer programs are designed by making them out of objects that interact with one another."

Key sentence above: "A feature of objects is an object's procedures that can access and often modify the data fields of the object with which they are associated"

The goal post was never moved. It was set by the Actual definition of OOP. I am explicitly including this feature to make sure that ALL we are dealing with is OOP and not some hybrid functional or OOP merger.

>(straw-man emoji)

This mechanism of turning macros into emojis doesn't work here on HN! Similar to your arguments.


The quoted text above is referring to objects, not OOP -- which encompasses a number of concepts. You can refer to the the "Features" section in your link above for the complete list (notice one is missing). For further information about the relationship between OOP and mutability just google it.

It's a waste of time to argue about a topic that is orthogonal to your original claim (before the goalposts moved): that the "problem" with OOP is how you invoke a function -- `noun.verb` as opposed to `verb(noun)`; As if those two systems are subject to different constraints. They are not. It is just semantics.

The above isn't to say there doesn't exist consequential differences between OOP and FP, but I absolutely did _not_ engage in this discussion to argue about OOP vs FP in general. I think we actually both agree FP is a superior paradigm.


>The quoted text above is referring to objects, not OOP -- which encompasses a number of concepts. You can refer to the the "Features" section in your link above for the complete list (notice one is missing). For further information about the relationship between OOP and mutability just google it.

Makes sense. Object oriented programming isn't programming revolving around objects. You totally invalidated all my claims with this genius argument here. You also invalidated the entire definition of OOP and wikipedia too.

>It's a waste of time to argue about a topic that is orthogonal to your original claim (before the goalposts moved)

At first I was going to say that the goal posts have not moved but your brain was the thing that moved. Then I realized nothing has moved. Your brain is still in the same place: A place of ignorance.

>that the "problem" with OOP is how you invoke a function -- `noun.verb` as opposed to `verb(noun)`; As if those two systems are subject to different constraints. They are not. It is just semantics.

Let me spell it out for you where the goal posts were when the conversation started. The goal posts were in a place where 99.999% of all programmers operate: In a universe of mute-able variables, mute-able objects and immutable functions. Typically "goal posts" start here because that's how 99.99999% of all programmers operate.

So within the bounds of these "goal posts" you made a claim that OOP and Functions only have a semantic difference. Which is CATEGORICALLY wrong. So then I decided to let you know that your CLAIM is only TRUE if VARIABLES are immutable you acted AS IF we were ONLY talking about immutable variables ALL ALONG as if that's the primary assumption all programmers make when talking about programming. The goal post wasn't moved... get it? I only changed the context a little to show you WHEN your arguments would be true, BUT nowhere did I "change the goal posts."

What happened after that is that YOU changed your argument. You imply that you've been ONLY talking about immutable variables all along and that I changed the goal posts to suit my needs. I can tell you 100% that you are either a liar or deeply delusional. You literally refuted a sourced definition of OOP in your attempt to stay relevant. Give it up dude.

>The above isn't to say there doesn't exist consequential differences between OOP and FP, but I absolutely did _not_ engage in this discussion to argue about OOP vs FP in general. I think we actually both agree FP is a superior paradigm.

I agree that we aren't talking about FP vs. OOP. What we are talking about is, and I quote:

"that the "problem" with OOP is how you invoke a function -- `noun.verb` as opposed to `verb(noun)`; As if those two systems are subject to different constraints. They are not. It is just semantics."

Which you are completely and utterly wrong about. You cannot make this statement without saying that all variables are immutable which is not a property that can be ASSUMED OUT OF THIN AIR.


Tone it down.

OOP is not "programming revolving around objects". Simply using objects in your code-base does not mean you are practicing OOP anymore than using functions means you are practicing FP. But I digress. Again, the definition of OOP is unrelated to the claim which prompted this discussion.

>The function would not be tied to state and that function can be executed in the context of anything

This is the (your) line of text at the top this thread (go look), and the point to which I wanted to offer some clarification. It has nothing to do with the definition of OOP or mutability vs immutability.

The point I have been trying to make is that `noun.verb` and `verb(noun)` are both tied to the same state; That the _caller_ of either of these functions needs to have a reference to `noun`. In this way, the "context" is also the same (both functions can only be executed in a context that has `noun`). There are problems with OOP! The above just isn't one of them.

>You cannot make this statement without saying that all variables are immutable

How do you know `noub.verb` mutates `noun`? Similarly, how do you know `verb(noun)` doesn't mutate `noun`? There is no way to make an assessment of the mutability of `noun` given those snippets. Which is kind of the point: they are equivalent.

Making an assertion about the behavior of either function (when that behavior is _clearly_ not shown) is building an argument against something else entirely. This is called a straw-man. Similarly, shifting the argument to be about the definition of OOP and/or mutability instead of the relationship between how a function is written and its dependencies/modularity is to argue about something else entirely. This is called moving the goalposts.


>How do you know `noub.verb` mutates `noun`? Similarly, how do you know `verb(noun)` doesn't mutate `noun`? There is no way to make an assessment of the mutability of `noun` given those snippets. Which is kind of the point: they are equivalent.

This is why you have to make every possible assertion. Muteable and immuteable. And in the muteable case your statement is wrong. Since we are talking about OOP (see first sentence of my first post) by definition only the muteable case is considered. There was no movement of goal posts, you simply failed to see where the goal posts were originally.

>Making an assertion about the behavior of either function (when that behavior is _clearly_ not shown) is building an argument against something else entirely. This is called a straw-man. Similarly, shifting the argument to be about the definition of OOP and/or mutability instead of the relationship between how a function is written and its dependencies/modularity is to argue about something else entirely. This is called moving the goalposts.

The topic at hand is on OOP. That's because that's the topic in my first post. Thus since OOP is mentioned in my first post THE DEFINITION of OOP is what I'm talking about. And since mute-ability is part of the DEFINITION of OOP this is the topic. What happened is you entered this conversation with the idea that the goal posts were somewhere else. They were not.

We are talking about noun.verb and verb(noun) in the context of OOP. That is the topic. And the statement of verb(noun) is the same as noun.verb is categorically wrong. You are wrong. This is concrete.


The only assertion I need to make (and have made repeatedly), is that `noun.verb` and `verb(noun)` are subject to the same constraints. In terms of mutability, we can say that both functions could mutate `noun` or both functions could not mutate `noun`. It is a straw-man to build an argument against a premise which arbitrarily asserts that `noun.verb` _behaves_ differently than `verb(noun)`. The behavior of the function(s) is not relevant to point you were trying to make or my point of clarification (of course two functions that behave differently are different!). Again:

>The function would not be tied to state and that function can be executed in the context of anything

The above is the claim which prompted this lengthy discussion. It has nothing to do with the definition of OOP or mutability. To rephrase your argument in my own words:

Separating data and behavior (state and functions resp.), in the form of `verb(noun)`, is superior to combining data and behavior, in the form of `noun.verb`, because it means `verb` is subject to fewer constraints and, as a result, is more portable.

The above is not accurate. There _are_ problems introduced by encapsulation. But one of them is not that functions are tied to state. This remains true for any function no matter how it is written (the semantics).

I'm not sure why you continue to insist on moving the goalposts here, especially to an argument for which you are so obviously wrong. The definition of OOP makes no claims about mutability. Furthermore, many of the most popular OOP languages provide built-in support for defining objects as immutable (while at the same time implementing immutable objects in their own standard libraries. e.g. `string` in many languages). Even a cursory google search on the subject unambiguously yields this information. I'm not going to waste my time debating this when you are fully capable of researching and revising your position. Especially because this has nothing to do with the claims that provoked this discussion in the first place.

My original comment was rather innocuous. It was intended as a point of clarification towards a claim you made as part of your broader opinions on the deficits that OOP impose on a system as it relates to functions, state, and modularity. You essentially say the same thing later in your post in your comparison between the OOP model and microservices:

>The difference here is rather then unionizing action and state with Code it unionizes state and action with real cold hard metal. The actual machine that holds state and "does stuff" is the object. So all the problems of realizing how to divide or unionize services and state in microservices are exact same problems that existed when trying to figure out what methods belong with what object in OOP...

Just replace "microservices" with "FP" in that last sentence and you will have basically synthesized my argument:

"all the problems of realizing how to divide or unionize services and state in FP are exact same problems that existed when trying to figure out what methods belong with what object in OOP"

For what it's worth, I agree with your ultimate conclusions.


>>The function would not be tied to state and that function can be executed in the context of anything

>The above is the claim which prompted this lengthy discussion. It has nothing to do with the definition of OOP or mutability. To rephrase your argument in my own words:

>Separating data and behavior (state and functions resp.), in the form of `verb(noun)`, is superior to combining data and behavior, in the form of `noun.verb`, because it means `verb` is subject to fewer constraints and, as a result, is more portable.

>The above is not accurate. There _are_ problems introduced by encapsulation. But one of them is not that functions are tied to state. This remains true for any function no matter how it is written (the semantics).

Have you ever thought about the difference between a list of "noun.methods" that encapsulate a "noun" vs. a list of "verb(noun)" that also encapsulate a noun? Under your semantic equivalence principle it's the same thing. IN your universe one can encapsulate a noun with procedural functions through type signatures AND it can be done with OOP as well. We are of course negating the usage of a noun as a global variable.

Also what is the "noun" in "noun.verb"? Can it be a namespace? Is "noun.verb" the same as "namespace.verb"? Namespaces hold variables and functions and so do Objects... Are these two concepts isomorphic? If regular programming already has namespaces what's so special about Object Oriented programming? What's the point of calling a namespace an object?

Well then you could say that OOP allows the user to "generate" or "instantiate" namespaces at runtime. That's neat except what is the point of generating a new namespace for what is essentially the same thing? Why do I need namespace1.verb and namespace2.verb when I can just have verb(namespace) or just get rid of OOP and have a single namespace.verb?

Well you could argue that namespace1 and namespace2 have different states. That's why you need the ability to instantiate them. But then what's the point of instantiating different namespaces at runtime when you can just have verb(namespace1) or verb(namespace2)? What's the point of OOP if it's the same thing as everything else in existence? Was OOP invented for semantic confusion only?

Well no, in OOP the namespace.verb() verb modifies the instantiated namespace. namespace1.verb is DIFFERENT from namespace2.verb because they are modifying different states. This is OOP. If VERB was stateless and did not touch the state of namespace2 or namespace1 then there would be no difference and you wouldn't be doing OOP.

It is mutation that ties a method to state. The goal posts were never moved. When you talk about OOP you have to talk about it in the context of mutation because that's what Wikipedia lists as part of the definition and that is the only unique and logical difference between OOP and other forms of programming. Thus you must be mutating or reading internal state to be doing OOP.

In the context of OOP and the sort example, noun.verb MUST modify noun otherwise we aren't talking about OOP. IN the context of procedural functions verb(noun), verb COULD modify noun OR it could NOT. In FP verb CANNOT modify noun ever.

FP: verb(noun), verb cannot modify noun OOP: noun.verb, verb MUST modify noun Procedural programming: verb(noun), verb can modify noun or it may not.

The implementation changes depending on the context and the context is 100% OOP. For any styles to be isomorphic (which is what you are implying) the properties must match exactly. I talked about OOP in my first comment, you were making a clarification about it in reference to another person who was also talking under the SAME oop context. The title of this entire thread has OOP in it. We are talking about OOP.

You can bend procedural programming to be isomorphic to OOP. But this isn't a two way equivalence, you cannot always bend OOP to fit a procedural function.

This statement from you (a perversion of a statement I made earlier) is ALSO categorically wrong:

>"all the problems of realizing how to divide or unionize services and state in FP are exact same problems that existed when trying to figure out what methods belong with what object in OOP"

In FP, everything has to be immutable. This the exact opposite of a required property for OOP. OOP uses mutability to tie methods with state.

Also you should note the definition of encapsulation is literally my argument of unionizing state with methods plus the added property of information hiding or aka private members and methods (https://www.wikiwand.com/en/Encapsulation_(computer_programm...). That's it. Since you're basically saying that the unionization of state and methods ISN'T the problem... you must be referring to information hiding. The private keyword, according to you is the problem with OOP. All of the problems with OOP (encapsulation) according to you could be fixed if people stopped using the private keyword. Amazing. Take a look at python... no private keyword... didn't fix anything.

>For what it's worth, I agree with your ultimate conclusions.

If you think encapsulation is the cause of the "ultimate conclusion" you need to realize, namespaces and modules and libraries also encapsulate things with no problems. The source of OOP's problems is not in encapsulation. You or whoever introduced that idea to you is completely wrong. Literally you can write a metaprogramming script that removes all the private keywords from objects and according to your logic will fix the problem.


typo up above:

>I am not just saying OOP is better than functional programming

I am not just saying functional is better than OOP




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: