20 ms·
In my mind, state is the real enemy impacting: comprehension, brittleness towards making changes, and the surface area exposed to potential bugs. OOP as freque
by taurath 5y ago
In my mind, state is the real enemy impacting: comprehension,
brittleness towards making changes, and the surface area exposed to potential bugs. OOP as frequently implemented, while claiming to encapsulate state, ends up creating so much more.
In accordance with this view, I think project architecture should be approached with an emphasis around how much state is necessary for it to run. This is why simulations like say someone making a game or simcity with like relatively independent entities that map to something in real life use OOP. If you're writing a service doing requests, you want as minimal state as possible. Singletons are state. Initialized/non-static objects are state. The smaller amount you have the easier it is to reason about the system.
As I write this however, I worry a little that my view is overly simplistic, or maybe applicable only to domains that I have worked in. If anyone wouldn't mind poking holes in this argument or offering examples I would appreciate it.
- lostcolony 5y agoI think in a lot of ways you're correct. FP and imperative code tends to makes state explicit; OOP hides state. The latter MAY make things "easy"; it never makes it simple.
- deleted 5y ago[deleted]
- zozbot234 5y ago"Hiding" state is necessary to endow it with well-defined invariants. This can be done in many FP languages, too. The semantics-side implications of "encapsulated" state w/ proper invariants have yet to be explored, though, and this is where newer PL formalisms like "homotopy types" might end up being quite helpful.
- dwohnitmok 5y agoWhat would homotopy types bring to the table?
- zozbot234 5y agoThey seem to be necessary if you want a notion of "equivalence" (for both values and types) that enables you to make functions, operations, constructs etc. independent of any notion of "underlying representation" as well as seamlessly applicable across equivalent 'representations'. This is desirable in both higher mathematics (where homotopy types were first developed) and software engineering, for much the same reasons.
- _fullpint 5y agoIsn't this what a homotopy category is for though?
- arrow7000 5y agoCould you explain this to a layman with an interest in FP?
- secdeal 5y agoI read about this a while ago, I'm not an expert but this is my take on it: In homotopy type theory an equivalence of types is a first class value that you can manipulate. And also you can separate types from their 'implementations'. Classic example is you have a Nat type, with a Peano construction (Nat is a zero or a successor of a Nat.) This is not very efficient, but you write functions with it, prove things etc. It's time to optimize, and you change your Nat implementation to something more efficient (e.g. a Nat is Zero, or twice a Nat, or twice a Nat + 1). Your functions and proofs that you wrote with the previous implementation still will work and your type signatures won't have to change
- astrange 5y ago> The semantics-side implications of "encapsulated" state w/ proper invariants have yet to be explored, though It seems that that's called a state machine, and OOP objects should come with state charts, but they don't. > and this is where newer PL formalisms like "homotopy types" might end up being quite helpful. PL research would actually get adopted if they didn't insist on using the worst possible names for everything. If they're not calling something an "intuitionistic type theory in the calculus of constructions" they're calling it a "pi-calculus".
- deleted 5y ago[deleted]
- zozbot234 5y ago> if they didn't insist on using the worst possible names for everything If the alternative is stuff like "FactoryFactoryFactory", I'm not sure that's better.
- 7steps2much 5y agoIt might not be better, but at least i don't need a dictionary to understand what it is all about, I just need to read it again and sort out the words in my head.
- zelphirkalt 5y ago> It seems that that's called a state machine, and OOP objects should come with state charts, but they don't. That is because the state chart would so quickly explode into uncountable states or so difficult to understand transitions from state to state, that such a diagram would become instantly useless. Which only goes to show, how unrealistic the idea is, that you can really fully understand such a system and that shows what the problem is. Granted, there may be certain areas of related state, that are separated from other areas, but when the program becomes non-trivial, the lines usually blur, unless some kind of approach is used, which reminds me very much of FP, only that it wraps functions uselessly in classes and objects, instead of making use of modules and functions only. In an FP style, ideally each function would be a thing you can look at separated from the whole system, if you know what its input can be (which may be difficult). That makes for testable code. I should be able to test every function separately, without having to use ten other classes to make instances to set up an environment, in which I just hope that what I wanted to test, can actually be tested.
- scrubs 5y agoAbsolutely. Why make const char * data private in a string class otherwise? I have know it's in a valid state so I can get it to the next valid state when caller runs an operation on it. But then a lot of proving (small p prove) an object in c++ is in a valid state amounts to hoare predicates and other spark-ada like expressions. Certainly FP could do same and like c++ define that away when callers and callees think undefined behavior is gone?
- taeric 5y agoTo continue my hot take from earlier, OO wasn't supposed to "hide" it anymore than FP was supposed to. Rather, OO was supposed to be about changing the metaphor of the program as you are writing it. This is most easily seen if you consider a TON of the domain specific languages out there. Logo, PostScript, DVI, GCode, etc. Many of these are "move to X" "put down pen", "move to Y", "pick up pen", etc. Very imperative and how you would talk to someone on how to do something. So, if your objects give meaningful verbs to control the state that they maintain, it works rather naturally to reduce the code that you have. Now, most OO today, that I see, embraced objects as records. And goes out of its way to not encode any language of behavior in the code they let you write. But, I don't think that is enough of an argument to say that abstracting some active objects into an OO paradigm is a waste and can never help.
- kaba0 5y agoI think you are exactly right. There is a huge difference between data-oriented and behavior-oriented parts of a program. OOP is only a great tool for the latter but it does allow for immutability besides it, it is not either-or. Wrap the behavior in classes and have data as data. Hopefully this will be indeed the direction taken by Java with its records and other new TBD features.
- deleted 5y ago[deleted]
- Supermancho 5y ago> ends up creating so much more. This is primarily because of inheritance, which seems counter-intuitive. In a meta-analysis of OOP-based designs, inheritance is used as the primary form of composition with other strategies being either last-resort or added later when the inheritance is already deeply embedded as part of the design. Inheritance is a brittle form of a composition (no-reinherit) that nests state in a deep tree-like type system, rather than isolating it into attachable modules. Most OOP-based languages have slowly had to adopt additional forms of composition, as inheritance is not suited well for cross cutting concerns. Ironically, almost anything added after the base class (and maybe some abstracts above that) is a cross cutting concern added after the core functionality is established.
- taurath 5y ago> This is primarily because of inheritance, which seems counter-intuitive I agree that inheritance creates a lot more problems, but the usages of non-static methods and internal state even in classes with no usage of inheritance can feel just as bad, when you have a high level method utilizing instantiated objects. Internal state as a whole can be avoided fairly often
- jshen 5y agoI don’t see much difference between some_state.do_stuff() do_stuff(some_state)
- lolinder 5y agoThey're just different syntaxes for the same thing. I think what OP is driving at isn't the syntactic difference but making immutable what doesn't need to be mutable. You could do that with either syntax.
- jshen 5y agoYes, but I think the person he/she was replying to is right, the biggest problem is inheritance.
- ummonk 5y agoGames are actually moving away from OOP by separating out state into a data oriented system.
- deleted 5y ago[deleted]
- Wohlf 5y agoIn my experience many game companies were never big on OOP in the first place.
- kaba0 5y agoWeren’t games already written with entity component systems?
- ummonk 5y agoI’m happy to be corrected as I’ve only really dabbled with Indie game development on and off but my general impression is that the paradigms have gone procedural -> object oriented -> ECS. For example, Unity is designed around GameObjects but is now adding building out the DOTS framework stack that’s more of a first party ECS system.
- seanmcdirmid 5y agoNobody has ever been able to convince me that these informal object systems (eg ECS) aren’t really just OOP with different extensibility mechanisms and/or a different place to stow away object state (oh, and let’s call the objects entities instead). The only real diversion from objects in games that I’ve seen is the work in Andrew Kennedy’s thesis on FRP for games.
- ummonk 5y agoYes and no. The EC part of ECS is definitely object oriented - more so than mainstream object-oriented systems / languages, in that it favors composition over inheritance (i.e. entities are compositions of components). But the S in ECS breaks the most fundamental tenet of object-oriented programming - encapsulation of state. And this separation between entities/components and systems is what makes data oriented programming so much cleaner and more powerful than OOP.
- deleted 5y ago[deleted]
- pklausler 5y agoMutable state, you mean, yes?
- taurath 5y agoI think so, though I had in mind internal (hidden/abstracted) vs explicit states. And yeah, only in a very few cases would you want mutable states - abstracting naturally transient entities like network connections, times where memory or performance constraints demand entity resuse, etc. Though if you end up in that situation with no guardrails around that state (IE: should usually be a finite state machine) you're definitely going to have to work a lot harder.
- rosenjcb 5y agoElegant Objects by Yegor Bugayenko actually argues against mutation in OOP for the same reasons that FP advocates due (bad for concurrency, hard to test, hidden state is hard to keep in your head, et cetera), but then all you get are namespaces with functions that act on a (usually) single data type (i.e. the class itself and its properties). OOP itself could be good for problems where you need state machines. The Erlang Actor model is successful for a reason, but I wouldn't apply OTP to general programming.
- pishpash 5y agoNothing that the typical junior engineer does with OOP can't be done with functions and well organized files. If they can't be trusted to do that well they shouldn't be writing OOP code.
- mypalmike 5y agoWhen everything's a function, junior programmers treat the entire set of available functions as reasonable things to call at any time. With OO, they at least have to think about how to organize things by which interfaces are available. People love to hate on Java, but a Spring app with everything set up into a graph of interacting interfaces is about the most well-organized code you'll ever see, and it encourages modifications that maintain thoughtful organization.
- agency 5y agoThere's a really wonderful talk that I've recommended to almost everyone I've ever worked with called Simple Made Easy[1] by Rich Hickey. I also struggled to explain why I hated state so much. You can talk about races with shared mutable state but even single threaded code I found I couldn't stand it, that it made things harder to reason about and change. It's because state is complex, in the sense Rich discusses in the talk: State intertwines "value" and "time", so that to reason about the value of a piece of state you have to reason about time (like the interleaving of operations that could mutate the state). I don't know if it's just me but I watched that talk a couple years into my career and it was like something clicked into place in my brain. It changed the way I think about software. [1] https://www.infoq.com/presentations/Simple-Made-Easy/ https://www.infoq.com/presentations/Simple-Made-Easy/
- cutler 5y agoMe as well but I was already sold on Clojure by then.
- allenu 5y agoThis is one of my favorite talks. It also helped things click for me regarding state. I try to use immutability wherever I can now and when there are unavoidable state changes, I try to understand and constrain the factors that could lead to such a state change. It's simplified things so much for me.
- usrusr 5y agoThat time part is what you are wrestling with when you are battling with state. So it's natural to think about it that way. But there's also this somewhat dumbed down version of the argument: every piece of state a method reads is like an additional function argument and every state it writes an additional return value. What a mess.
- branko_d 5y agoThis is insightful. In some sense, the only distinction a "pure" function has over "non-pure" is that it declares all its inputs/outputs (as function parameters and result). We say that a non-pure function has "side effects", but all that actually means is that we don't readily see all its inputs/outputs. Even a function that depends on time could be converted to a pure function which accepts a time parameter - this is conceptually the same as a function which accepts a file, or an HTTP request or anything else from the "outside world". The trouble, of course, comes from the tendency of the outside world to change outside of our program's control. What do we do when time changes (which is all the time!) or file, or when the HTTP request comes and goes never to be seen again? Or when the user clicks on something in the UI? Can we politely ask the outside world for the history of all past clicks and then "replay" the UI from scratch? Of course not. We cache the result of all these clicks (and file reads and network communications and database queries...) and call it "state". When the new click comes, we calculate new state based on the previous state and the characteristics of the click itself (e.g. which button was clicked on). This is a form of caching and keeping a cache consistent is hard, no matter what paradigm we choose to implement on top of it. The real-world example of this would be React. It helps us implement the `UI = f(state)` paradigm beautifully, but doesn't do all that much for the `state` part of that equation which is where the real complexity lies.
- stickfigure 5y agoIf only we could completely eliminate state! Thankfully, I am working on a plan for this. It should take around 10^106 years... give or take. The serious comment here is that the real world imposes a minimum floor on the amount of mutable state that you have to model. Databases are giant piles of mutable state. Maybe we should start talking about "essential state" and "accidental state" the way we talk about complexity.
- jkaptur 5y agoI agree and would add that the UI is also a pile of mutable state. Even if you model the DOM using a pure function, there's still scroll position, selection, animation, history, and so on. At the end of the day, users interact with state. We need languages and techniques that manage it well.
- native_samples 5y agoYes. This is one of the core problems of FP style analysis of what's "wrong" with software development. Sometimes those people act like state is some sort of bad habit, like chewing tobacco. But the way they say to get rid of state really just hides it behind large state-management engines, like browsers and databases. It boils down to "state for thee but not for me". Well, great. But some of us have to deal with the fact that computers are often used to model the real world, which isn't a pure function.
- taurath 5y agoI agree with this - and of course without any state whatsoever the program is unlikely to be useful. A database, a network connection pool, and initialized configurations are required pieces of state for just about any backend service, and you can't really get rid of them. But having clear lines around how state is stored and utilized and minimizing it in business logic to me creates a much more sane program.
- kaba0 5y agoIsn’t exactly these boundaries what OOP gives us?
- reificator 5y ago> This is why simulations like say someone making a game or simcity with like relatively independent entities that map to something in real life use OOP. I don't think this is the case, or at least it hasn't been for quite awhile. Any gamedev I've known in the last decade or so would reach for an ECS[0] if they wanted to clone SimCity, in other words they would design it somewhat like a normalized database. Each character, building, zone, or whatever in the game would be an Entity, represented by a unique ID. Then there would be collections of Components, which are basic structs like Position or Sprite. A set of components that are all tied to the same ID would represent a single Entity the same as if it were an instance of an Entity class. These components together hold all of the game data, to the point where a naive save game system could just serialize all the (non-pointer) component data and be done. How these are stored varies by implementation and configuration, but a table in a database is a reasonable mental model to understand the core concept. The game logic is executed in Systems, which are functions that read and write component data on some schedule. The simplest example is a velocity system, that would find all the entities with both a Position component and a Velocity component and update the Position accordingly. In the case of a SimCity style game, the ECS approach is much more cache friendly, for both instructions and data, because you're handling all of the same work at the same time, instead of updating each entity one at a time which leads to cache miss after cache miss. This can bump the max number of agents in your simulation by multiple orders of magnitude. Some other benefits are: * Empower designers to iterate more quickly by giving them an editor where they can change components out without changing code. Say you have Players and Enemies and they both have Health, and Walls which are simply props with Collision. If you want to try destructible environments you can simply add a Health component to your Walls in the editor rather than try to move Walls into your LivingEntity inheritance chain or modify everywhere that does damage to check for WallEntity in addition to LivingEntity. * Easier to parallelize. If you're using objects and you want to start multithreading you quickly start feeling like mutexes are the only answer. But with an ECS if your Systems only operate on their arguments, then you can run any systems in parallel that you want, as long as anything you mutate is not referenced in any other currently running systems. For instance every Rust-based ECS I've ever seen does this out of the box, because they can tell what fields are mutable from the function signature. * Easier to test. If all your movement system cares about are entities with both Position and Velocity, then that's all you need to setup to perform a test. No MockPlayerInput or headless rendering required, except where those are actually the thing under test. [0]: https://en.wikipedia.org/wiki/Entity_component_system https://en.wikipedia.org/wiki/Entity_component_system
- mabbo 5y ago> I worry a little that my view is overly simplistic, or maybe applicable only to domains that I have worked in. If anyone wouldn't mind poking holes in this argument or offering examples I would appreciate it. I think this line alone is proof that you're doing this right.
- 762236 5y agoI believe that we're interacting on a server that was written to these principles.
- a-dub 5y agoif you're writing something like a wrapper service around a database, there should be no state at all. (i'd argue it's high time that databases moved forward with respect to security and hardening such that they can be accessed nearly directly or... directly) if you're building a thing where state is required, then yes, it should be minimized. but i'd argue against dogmatic use of immutable data records and really think about what that sort of design is attempting to achieve: reduction of sprawl in places in the code where a piece of data is updated. the goal is to put all that stuff in one easy to find place. that can be manifested or violated (sometimes with great acrobatics) regardless of whatever rules are followed. (even oop!)
- scns 5y ago> it's high time that databases moved forward with respect to security and hardening such that they can be accessed nearly directly or... directly Check out Hasura, Postgraphile and Postgrest.
- taeric 5y agoHot take: OO is more powerful when you embrace stateful objects. As long as you are dealing with stateless objects, many other techniques have plenty of advantages. But, consider, OO grew in a time when the likes of Logo was strong. How do you draw a square in Logo? Usually, some form of: pen down repeat 4 straight 10 right 90 degrees But this /only/ works if you keep track of the state of the system in your mind, when your are figuring it out. Which, works really well if you are being taught that your program doesn't exist in and of itself, but to manipulate something else. Functional advocates lose many learners because they don't allow that writing a local function can be done with the more global state in mind.
- toomanydoubts 5y agoA polygon is a set of lines A square is a polygon with four lines of the same length, forming 4 equal angles of 90° A drawing can be made given a pen and a shape The output is a drawing of square made with pen
- taeric 5y agoAnd a line is a set of points. Your point? That is, yes, to an extent you can expand your vocabulary to include more shapes and find ways to compose them. Or you could peel back and literally write point values. Both have uses. And both are used. Consider, we aren't forcing all art assets to be created and described using elementary shapes. For many of those, we are closer to literally writing the canvas by hand. And then retroactively glueing to program. It is a shame we often force a single paradigm with our code bases.
- snovv_crash 5y agoBut then you're mixing up the state of the system with the shape you want to draw. If you were now working with 2 pens, you'd have to rewrite your shape from scratch too, not just your rendering, to speed up the output. Better to separate the shape data, which is immutable (and basically declarative), and the rendering method, which does need to know about the previous work which was already completed and what it is doing right now.
- sunjester 5y agoPreach it brother.
- eschaton 5y agoWow, now we just need to stop interacting with anything stateful, like the real world!
- halpert 5y agoSorry, but state is everything. If you don’t have state, then you’re essentially doing useless work computing an answer that is already known. Computation is only useful because of state.
- jay_kyburz 5y agoThis is what I don't understand about the mutable vs immutable, my functions only exist to mutate state.
- brabel 5y agoWith mutable state, your function mutates state somewhere in the environment (typically a global variable or the class in which the function resides). With immutable state, your function returns an immutable value, never modifying anything. I program Java for the most part, and this is how I do most things. Mutable state is the enemy, not OOP. Some people think mutable state and OOP are inseparable but they are just looking at a definition of it that is easy to criticize. If you make every object immutable , OOP actually becomes a much nicer model to work with.
- kinjba11 5y ago> only exist to mutate state Which part of the state, and when? Is this your program? // change anything, anywhere, in the entire database, the biggest state execute_sql(user_input) You might want controls around that state so not anyone can change it. It might be read only for some users - that is, immutable. Is this your program? log_to_database(financial_event for $5.00) You probably want your financial logs to be immutable. Nobody should be able to mutate that $5.00 event to be $500.00. Is this your program? o.foo = complex_function(...) ... 1000 lines later ... o.foo = null ... 1000 lines later in another file ... o.foo.do_stuff() // oops! foo was set to null somehow - but where? The above scenario has shared state with "foo". Somewhere it was set to null somewhere. It could be set to null anywhere in your program. Good luck tracking the bug. If "foo" was immutable, you would know immediately where null came from, because it can only be initialized one time. It lowers the cognitive load, knowing that certain actions are impossible makes it easier to focus on what matters. Is this your program? thing = computation() return thing + 2 Many program are a series of computations - fresh state is created on each line, nothing is mutated. There is no reason not to be using immutability. In fact, if immutability were throughout, a compiler wouldn't have to worry about things like aliasing. This is all the tip of the iceberg, there are many reasons to enjoy using immutability. https://en.wikipedia.org/wiki/Aliasing_(computing) https://en.wikipedia.org/wiki/Aliasing_(computing)
- xupybd 5y agoI like the Elmish way of explicitly managing state. You have one model that changes. Any model state can be rendered. The state is obvious and clear. If you want to test something just creat that state and test it. No need to click 10 buttons just to get the UI into the state where your bug was found.
- hota_mazi 5y agoI used to think that the solution to mutable state was to prohibit it and code with immutable structures all the time, but after a few years of Rust, I think it's the wrong approach. The right way to handle mutable state is not to pretend it doesn't exist but to accept it as a reality of complex systems and to encode its management in the type system of the language. And that's exactly what Rust does. With Rust, I no longer feel dirty whenever I have mutable state and I trust Rust to not just keep my code bug free but also to make me think carefully about mutable state and how to design my code with it in mind.
- zozbot234 5y agoRust prohibits shared mutable state as part of the basic language (or rather its 'safe' subset), relegating it to special-cased "interior mutability" constructs. This is essentially "as immutable as you can get" in a low-level, systems programming language. (Other languages can thread state mutations explicitly as part of a generally "immutable" design, but that doesn't give you support for the expected low-level features, so instead it's part of the language in Rust.)
- deleted 5y ago[deleted]
- carlmr 5y ago>project architecture should be approached with an emphasis around how much state is necessary for it to run. This is why simulations like say someone making a game or simcity with like relatively independent entities that map to something in real life use OOP. In the beginning of my career I did a lot of engineering simulations (Simulink), to me signal flow diagrams have always been a very obvious way to model programs. All the state is explicit in that it becomes a delayed output->input mapping of the signal flow graph. Each block behaves the same, because it has no internal state. I always thought about programs in the same way. What goes in, what goes out, what goes back in (if multiple iterations). Only later did I find out about functional programming, which basically is the same idea, and that instantly clicked. Except for the auto-completion after writing the . on an object, I've never really seen OOP (in the Java way, not the Erlang way) be intuitive or simple. Always keeping state in mind, class hierarchies spanning tens of files where the only way to know what your object really does is step through with a debugger, interfaces for everything because otherwise you can't mock the classes for the test, the list goes on.
- ajuc 5y agoThis is true, and another thing about state is also true - relational databases are much better suited to handle state cleanly and to minimize the amount of it than programming languages (except maybe prolog). Especially OOP languages are bad at state minimalization. For example there's no commonly used equivalent to normal forms in oo. There are no indexes and no materialized views. The funny effect is - if you want to minimize state and you're serious about it - keep everything you can in database and make a 2-layered (relatively thin) client <-> relational db architecture. With stored procedures. We were there in 90s and we moved away because web and OOP became fashionable. So we took our clean, normalized, minimal state from db and made it messy and complicated with ORMs to satisfy OOP gurus :)
- kaba0 5y agoI don’t think ORMs are fair choice as prototypical examples of (good) OOP. But I agree that having data (record) primitives is very important in some given domains (and that relational databases are really powerful). There are other cases as well which are not as data-oriented though.
- ajuc 5y agoORMs were the OOP way in 00s and 10s.
- hardolaf 5y agoPeople also moved away from that paradigm because databases are slow. I work in the world of optimizing TLP level communications over PCI-e buses. To me, a database access is already in the world of "why bother?".
- grumpyprole 5y agoI personally think "encapsulation" as us used in OOP, is a misnomer. State is usually not encapsulated, it is just hidden. Proper state encapsulation would be to use mutable state internally for efficiency, but for that state to be unobservable externally. OOP does unfortunately encourage introducing mutable state into the domain model. The canonical example being the back account, with a mutable back balance! The good parts of OOP are interfaces and first-class modules. Obviously we should try and keep those.
- dvt 5y ago> Proper state encapsulation would be to use mutable state internally for efficiency, but for that state to be unobservable externally. This is literally how private/public keywords work, so I think your criticism is unfounded. However, I do agree with the overall sentiment that OOP implementations tend to "leak" way too much state than they need to.
- grumpyprole 5y agoI think you misunderstood my point. Private may protect direct access to mutable state, but the object may still have mutable state that is observable externally and must be reasoned about. In which case, the mutable state is not truly encapsulated.
- kaba0 5y agoThat is not hidden by immutable data either - the state is just global in the latter case, in a way. There is no getting away from essential state, from a theoretical point of view. In my opinion the often leaky partial encapsulation of state is still one of the better ways to deal with it. And immutability is another axis so of course the frequent case where it makes sense should be adhered to, but not religiously.
- grumpyprole 5y agoIt is much easier to reason about immutable state than mutable state, both for humans and compilers, especially in a distributed setting (any modern piece of hardware). The problem with "leaky" encapsulation as you put it, is the combinational explosion of the state space as many stateful objects are composed. Most mainatream programming languages are still unfortunately not well geared up for working with immutable data, they lack even persistent collections. Functional languages are of course ideal for this.
- ahartmetz 5y agoI have come to the same conclusion. State is the problem. State should be: - minimal (amount and lifetime) - well conceptualized (~= easy to understand the organization) - well named - minimally exposed - coherent by construction (make inconsistency impossible by design of the format or by offering updating functions that ensure the invariants) OOP can actually help with some of these things! I develop mainly in C++, which doesn't encourage a purely OOP style like Java.
- rawoke083600 5y agoI like your "bullet points" and agree with them all. Wat are your thoughts on (super simplified example): * 1 state-var with 3 values ? * 2 state-vars with 2 values each ? Sometimes I steer my design too much to first example and then other times to the last example. Both extremes can make things ugly
- ahartmetz 5y agoThis is a typical conflict, and I think my main problem is that I spend too much time worrying about it. The important thing is that you make sure that they cannot become inconsistent (you can do this by always going through a function that ensures that when updating them). A thing I have done somewhat recently is: enum AuthConnectionState { WaitingForConfig = 0, Disabled, Connecting, Connected, TimedOut }; where the value of the corresponding variable is derived (in just one place that is called when any input changes!) from many inputs, and it's the authoritative source of information. If you want to know whether the current state allows to proceed with login (which can be local if so configured or the connection definitely failed), call: static bool connectionStateAllowsLogin(AuthConnectionState state) { return state == Disabled || state == Connected || state == TimedOut; } (Note for people who don't know C++: this is a file-static function, which is basically as private as it gets in C++, and it's also a pure function, not by any language feature though. It could access globals.) It has a couple of sister functions like isWaitingForWhatever() or isLocalLogin(). The naive alternative is a very nasty and error-prone forest of booleans, each of which you must remember to update when something about the connection changes, and to make sure it's all consistent. It's almost impossible to get right without exhaustive testing.
- vbezhenar 5y agoWhat is state? Every local variable is state. I can convert any global variable to a local variable or function argument with trivial changes. Does it mean that every local variable is the real enemy? Only empty program is perfect. But useless. I'd say that not just state is the real enemy. It's all about life time of a particular bytes. `(x, y) => (x + y)` is a good function. Its state is discarded pretty fast. `(x, y) => (z) => (x + y * z)` probably is a bad function, its state is preserved for a long time. Global variable state is preserved during the whole program run. Database state is preserved during the whole software lifecycle or may be even more. And that's not even about state is enemy. It's about how much time do you need to design this particular code. When you're thinking about database structure, that's a real deal. Take a lot of time, that's important and decisions will have impact for years or even decades. When you're designing pure function which does not leak any state, you don't need to think at all, just slap something and move on, you can easily replace it later if need arises. TLDR: prefer state with short life time, be very careful with code that works with long time state.
- Lutger 5y agoYour view isn't simplistic, I think a vast majority of developers would agree with it. State, however, is necessary for the purpose of creating useful and in some cases performant programs. Furthermore, in some cases state can make a program quite a bit easier to write and even read. I think there are much more interesting things to be said about state than just to minimize it. For example, you can limit stateful computations inside a function in such a way that the function itself is still referentially transparent (it behaves as if it wouldn't have state). In this way, you can still do a for-loop, or do a quick-sort on a copy of the input data, without losing the benefits of pure functions. In the D programming language, this can be expressed in the type system. We need to 'deal with' all the risks that state involve, reducing it is the first thing to do but then there are a lot of other options as well.
- rbanffy 5y ago> OOP as frequently implemented This is the real issue with OOP. Abusing a type system, using inheritance when composition or interfaces would be more appropriate, etc. I've seen pretty much every programming silver bullet implemented in the most horrifying ways by people who didn't understand the reasoning behind each approach. You can write great FORTH and terrible LISP. You can write readable FORTRAN or APL (I'm stretching it a bit here) and elegant 6502 assembly. You can even write resilient and reusable JavaScript and PHP if you have the discipline to do it. > If you're writing a service doing requests, you want as minimal state as possible. The service can have a lot of state. What you really don't want is your client trying to keep track of it. When tempted to do so, you need to change the service.
- Fire-Dragon-DoL 5y agoIt's definitely that, but to be fair the problem is caused by classg-based programming, not OOP itself. Put state on an object only if there is a hard requirement for it. The occurrence is incredibly rare, state is mostly introduced to save re-typing method arguments...