3 ms·
In functional languages you can still ensure that only certain functions can change certain parts of the state. The big idea is that state is isolated. You ca
by sodapopcan 5y ago
In functional languages you can still ensure that only certain functions can change certain parts of the state. The big idea is that state is isolated. You can actually write functional code that looks a lot like OO code (essentially just calling `foo(thing)` instead of `thing.foo()`) but because state is isolated, you know that nothing is changing behind the scenes. It may not seem much, but it's actually really freeing to not have to think about that stuff anymore. Same thing with immutability.
I was skeptical of functional for years and was actually a big OO zealot. I finally brought myself to give functional a serious go over 1.5 years ago. I now write it professionally and don't miss OO even a tiny bit. It's really just something you have to try and have an open mind about. I actually still find myself sometimes thinking, "Crap, maybe I should make a copy of this in case something else tries to touch... OH WAIT! IMMUTABILITY! I'M SAFE!" It's a really nice feeling :)
- AnimalMuppet 5y agoLet me separate out immutability for a moment. If I don't have immutability, then if I can call foo(thing), then I can write bar(), and then call bar(thing). Bar can now alter thing. So my encapsulation can be broken by someone just writing a function. (This is the same problem that C had with structs - anyone could write a function to alter the data in your struct, and thereby put it in an inconsistent state.) Now, with something like C++, you can still do that. You have to go to thing, though, and write a new thing.bar(). It therefore becomes much clearer that bar() may break the consistency guarantees of thing. So I don't think that just functional gives you the guarantees that OO encapsulation does. Now, immutability changes things... a little bit. But with immutability, the problem only moves, it doesn't go away. Someone can now write a bar() that returns a new/altered thing, and then pass it to me. I can still get a thing that is in a state that violates the rules for what a thing is supposed to be. And, once again, the same thing can happen with OO. It's just that, if it happens, you have a lot less code to look through to try to figure out how and where it happened. You may have noticed that I care a lot about data being in a consistent, valid state. If you don't care about that, then my arguments may not resonate with you.
- ummonk 5y agoYou can’t separate out immutability because without immutability it’s not functional code, it’s just procedural code. To the question of consistency though, OOP gets you very little because consistency rarely maps directly to objects. So if you end in a situation where object A is inconsistent with the object B, you still have to trace out all the locations where object A and object B might have been mutated and figure out what combination of code causes the issue. At least in the global state system you can get runtime consistency by running consistency checks on each attempted transaction.
- rhdunn 5y agoThere are two types of "data" in a properly encapsulated class [1]: 1. Internal/private data -- things like a pointer to a string's contents, its length, and the capacity of the content buffer. 2. Public "data" (API) -- best provided as accessor methods/properties (and public methods) to ensure that the class' invariants hold if these can be used to mutate state. You don't care if the object has been mutated through this public API as this is the contract for how the class/object instances should be used. A properly encapsulated string class is free to change its internal state/data (e.g. start+length+capacity, or start+end+endOfBuffer), as long as it keeps the contract defined by the public API intact. The same applies for data structures, mathematical objects like complex and rational numbers. You could store a complex number in polar (r, theta) or cartesian (x, y) form and provide public accessors for all of those values. If you had setters for those, the native representation would be a simple assignment, while the other representation would do the necessary polar <=> cartesian conversion. This would maintain the invariant that the two representations are equivalent, such that if you set r then the angle (theta) does not change, but the magnitude changes such that it is equal to r. Note that if the complex number (or string) was modelled in a procedural or functional language, you would have to choose and stick to one or the other representation. That structure or type definition is leaking the state, such that a program could modify the length of the string without updating its contents. Q: Can you give an example of where having object A inconsistent with object B is an issue? That would help me to understand the issue/problem you are referencing. [1] Many OOP languages also allow protected data, which can be seen by implementations but not any other code. These are risky, as they can allow the derived class to break any invariants on that protected data like you described in your second paragraph. As such, I try to avoid them wherever possible in my own code, but will run into them in thirdparty or system classes.