5 ms·
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 latt
by kingdomcome50 7y ago
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.
- crimsonalucard 7y agoI'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.
- kingdomcome50 7y agoI 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).
- crimsonalucard 7y ago>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.