5 ms·
The problem with state in objects is that in more complex systems you often need to change a bunch of related state at one time. When it's all bound up in indiv
by cageface 4y ago
The problem with state in objects is that in more complex systems you often need to change a bunch of related state at one time. When it's all bound up in individual objects that manage their own state this becomes very complicated.
- LAC-Tech 4y agoSorry, I don't get what how that's harder. newData = f(oldData) VS newObject = oldObject.f()
- stickfigure 4y agoFurthermore, it chains nicely and offers pleasant autocompletion in your IDE: newObject = oldObject.f().g().h().i().j().k() VS newData = k(j(i(h(g(f(oldData))))))
- LAC-Tech 4y agoJust to steel man the "Functions and Data" side of the equation in a lot of FP languages that'd be something like newData = oldData |> f |> g |> h |> i |> j |> k
- vanjajaja1 4y agoAnd to straw man the OO way a little... newData = oldData.f().g().h().i().j().k() wont work by default unless you also happen to return the object after every method (builder style) which looks weird and is not standard
- imtringued 4y agoI don't know what you mean. You can return a different type, the only constraint is that the methods don't return void and the subsequent method exists. This has nothing to do with the builder pattern.
- zelphirkalt 4y agoYou still have to do work though, to ensure, that always some fitting object with the correct methods is returned, so that you can chain further. Functions by definition (as opposed to procedures) return something and as long as that is the correct thing or type of thing for the next function, all is fine.
- stickfigure 4y agoYou don't have to do work; at any step you have the functions available that operate on the object returned (whatever it is). On the "Functions and Data" side you are similarly constrained by the possible functions that operate on that data.
- zelphirkalt 4y agoWhat I meant by "work" is, that inside your methods you have to return an object, which has the method, which is the next thing in your a().b().c()... chain. Often in OOP you would not return the object itself, unless you do the builder pattern. Of course you can return other objects, if it is intended, that the chain has a call to a method of another object next. However, you may only call methods of the returned objects and you must return objects. In the function and data scenario, all that needs to be made sure is, that your functions have the return type, which the next function expects, otherwise it is a type error, regardless of whether the language complains or not. A function call chain implicitly assumes the types to be correct, otherwise you will likely run into an error. The difference is, that in the functions and data scenario, you already have functions, which take data of some type and return data of some type. You do not change them in any way, while on the other hand with objects and methods you need to change the methods, so that they do return some fitting objects.
- LAC-Tech 4y agoThere's no law stopping you returning a new object from a method outside the builder pattern. Immutable objects are a pretty old idea, smalltalk had select (map) methods that returned a whole new collection back in the 80s.
- singularity2001 4y agothis has nothing to do with OO, function chaining depends on the output of the previous function regardless
- zumu 4y agoChaining is one way of making function composition more ergonomic, but there are others. Functional languages tend to have an operator (e.g. |> in Elixir. Think x |> f() |> g() |> h() |> i(), etc.) that gives the same benefits of chaining, but the functions don't have to be methods providing greater flexibility.
- xigoi 4y agoYou can have the chaining syntax without object-orientation, like in Nim, D, Koka, VimScript, …
- cageface 4y agoRight that's one object. Often you need to change the state of a bunch of related objects in a transactional way. The way redux works for example. It's somewhat analogous to database transactions.
- LAC-Tech 4y agoNot sure what you mean here. Redux changes the state of one piece of data - the thing inside the redux store. Really not hard to imagine that with one big object in the exact same fashion.
- randomsde 4y agoI’m not a functional programmer by trade, but I think you’re alluding to (and should reframe your argument to discuss) concurrent reads and the implications of immutable data in a multithreaded scenario. It’s about simplifying your programs state machine. If I have some object A in an OO world, some N threads may act on said object concurrently resulting in some degree of non-determinism for any function of A. In FP, as I understand it, this is mitigated as you’re not dealing with mutable data structures but immutable objects whose data is consistent over the lifetime of any logical operation. Effectively, you just need to deal with conflicts on writes which you can design for. This is just a side effect of functional programming but such behavior can be embedded into and made a fundamental part of your foundational data structures in an OO program.
- zelphirkalt 4y agoCan I make a stupid example? Lets assume you got some building with people inside. One person exits the building. mybuilding.leave(person) or so. What does .leave(...) do? Ah, it needs to reduce the counter of people in the building by 1. Oh and by convention this means, that the person is checking out, so lets update their schedule to account for that. Ah they are also currently "out of office". Ah and the person at the door, who keeps watch has now seen another person leaving. Maybe the person went through a scanner. The scanner statistics need to be updated. And so on and on. All kinds of objects need updates for their states. You need to direct all this stuff, without forgetting things. Otherwise the state of your objects will become inconsistent and at some point it will show as some kind of bug. At that point you might find yourself debugging the whole thing, to find out when the inconsistency starts or what causes it. No need for multiple threads or any of the kind. Complications from splitting the state up and parking it in various objects and having to tell the computer how to update these objects. Instead of looking at 1 function at a time, which only works on its inputs and gives an output, you have to remember to call all the appropriate methods across some reasonable part of your entity landscape.
- kaba0 4y agoDepends on the problem. Sometimes the locality of every state inside the function you write is beneficial, other times it makes it much easier to just not care about the state of a given thing, and let the encapsulation do its job. I just want to use it as per its contract.