4 ms·
> In OO the core concept is the mutable object. I disagree here. In OOP the core concept is the object (whether mutable or not) understood as a set of data and
by kaoD 10y ago
> In OO the core concept is the mutable object.
I disagree here. In OOP the core concept is the object (whether mutable or not) understood as a set of data and accompanying functions which operate on it. This is where OOP and FP collide: FP strives for code reuse through universal functions which operate on heterogeneous data.
In practice in FP data has to adhere to a certain structure, which is not very different from OOP's classes/interfaces/traits after all. FP just ditches classes of objects as a construct, opting instead for non-encapsulated interfaces into generic data structures, whether expressed explicitly (e.g. typeclasses) or implicitly (duck typing).
This duality is exposed in languages like JavaScript, where class methods are just regular functions dynamically bound to `this`. A class instance is, after all, equivalent to a bunch of functions closed over its members. I.e. OOP is just syntax sugar to encapsulate data and functions.
Mutability is just a (leaky) implementation detail, completely orthogonal to OOP or FP. A class with no internally-mutating setters is still a class. A method with immutable bindings is still a method.
> If objects are mutable then the order of execution matters
Again, completely orthogonal to mutation. The order of executiong always matters:
(cons :a (cons :b '())) ≠ (cons :b (cons :a '()))
I agree that function composition is nicer than a bunch of statements (in certain cases!), but the burden is still on the programmer to get it right, whether dealing with mutable data structures or not.
What FP languages get right is making side effects (including mutation) a second-class citizen, but that's completely orthogonal to OO.
- pherq 10y agoThe code example you give isn't an example of evaluation order, it's an example of different expressions not giving the same result. Order of evaluation being irrelevant means that when evaluating (cons 'a (cons 'b nil)) it doesn't matter whether you first create the outer or inner cons cell. Or, for that matter, whether you do anything at all at this point (you could for instance just suspend the entire expression as a thunk and only evaluate them when you try and access values within the list -- i.e. lazy evaluation).
- kaoD 10y agoIt's not an example of evaluation order because we weren't discussing evaluation order. GP said: > If objects are mutable then the order of execution matters, and you have to trust the programmer to get the data flow and control flow to agree Which is true both for OOP and FP, unless I misunderstood what GP meant with data and control flow.
- chriswarbo 10y ago> (cons :a (cons :b '())) ≠ (cons :b (cons :a '())) In less idiosyncratic syntax, this says that the list [a, b] is not equal to the list [b, a]. That's the case in all languages (at least, it should be!). The order of execution is about the same value being calculated along different paths. For example, we can consider the value [write("foo", /tmp/x), read(/tmp/x)]. Here's one path: [write("foo", /tmp/x), read(/tmp/x)] [null, read(/tmp/x)] [null, "foo"] Here's another: [write("foo", /tmp/x), read(/tmp/x)] [write("foo", /tmp/x), "previous content"] [null, "previous content"] These values aren't equal, so the path taken by the interpreter/compiled-code can affect what our program does! In contrast, we could follow an approach like Haskell and encapsulate IO actions as standalone values. This gives one path: [write("foo", /tmp/x), read(/tmp/x)] [<Some IO action>, read(/tmp/x)] [<Some IO action>, <Other IO action>] Here's another path: [write("foo", /tmp/x), read(/tmp/x)] [write("foo", /tmp/x), <Other IO action>] [<Some IO action>, <Other IO action>] These are equal, so we don't have to worry about the path taken affecting out outcome. We do have to worry whether the chosen path will produce an outcome at all, since it's possible that a program gets caught in an infinite loop even if it's avoidable. We can remove this worry if we use lazy evaluation.
- kaoD 10y agoYou're nitpicking. GP said: > If objects are mutable then the order of execution matters, and you have to trust the programmer to get the data flow and control flow to agree My point is that, even in FP, as soon as you hit sequential operations (in your example, side effects), you're still bound by the execution order and the programmer getting it right (and how that is completely orthogonal to OOP or FP). Order-dependent sequential operations are the programmer's responsibility regardless of paradigm. I don't think OOP is inherently sequential. Your post made me realize GP is conflating OOP and imperative programming and his post makes sense if :%s/oop/imperative programming/.
- eternalban 10y ago> I disagree here. In OOP the core concept is the object (whether mutable or not) You've missed OP's deeper point: even if immutable, the duality of object identity & object state remains. (Immutability is not the central issue. The issue is that in FP you can use the term "data" without blushing, where as in OOP "state" is masquerading as "data".)
- kaoD 10y agoNo I didn't miss it, I just don't think that duality is inherent to OOP and is pretty much a byproduct of OOP languages being imperative, mutable and referential, which makes OP conflate all those properties. What do you think prevents OOP languages from implementing automagical non-referential equality by data traversal by default like FP languages do? Is it some property of OOP itself? IMHO it's not a property of the paradigm. The duality is still present in FP languages, it's just hidden under a nice automagical layer. (= (cons :a (cons :b '())) (cons :a (cons :b '()))) => true Both lists are still separate memory regions. They're the same data only in a platonic way and there's nothing that prevents OOP from implementing the exact same abstraction.