18 ms·
Usually when I see somebody arguing against object-oriented programming, I don't see them arguing for functional programming, but instead arguing for procedural
by commandlinefan 6y ago
Usually when I see somebody arguing against object-oriented programming, I don't see them arguing for functional programming, but instead arguing for procedural (like Cobol, Basic, or Pascal) programming. What they usually miss is that there's a reason procedural programming was abandoned some time around the mid-90's, and for good reason: you can't realistically develop useful software in pure-procedural way without introducing a lot of global state. Even if you look at well-designed non-OO code like, say the Linux kernel, you'll see that there are object oriented concepts like information hiding and polymorphism all over the place; they just don't formalize them with the "class" or "private" keywords. Unfortunately, what I see most programmers do is give up on OO design (and never even consider FP) and instead create global state that they call "singletons" to pretend that they didn't just create a global variable. Because, as we all know, global variables are bad, but too few of us actually remember why.
- etangent 6y agoThere is a (mistaken?) tendency to see OOP as a step beyond functional programming, probably because it allows for more complicated abstractions. But it seems to me a more natural scale to use is how the state is managed. In procedural programming state is global, which is messy. OOP cleans it up a bit by splitting global state into pieces and making them local. Functional programming takes this even further by avoiding having to reason about state altogether.
- Jtsummers 6y agoProcedural code doesn't require global state, though it doesn't discourage it either. The mid-point between procedural global and OO encapsulated state is the use of structures/records to contain state, but without any associated (that is, no language enforced association) methods/functions. You instantiate a new state record, and pass that around. This is a natural way to work on procedural code (in an iterative development fashion) to move from prototype/early code to stable, maintainable code. You create global variables, then encapsulate them in a record (perhaps keeping just one record globally), then you move to passing a record around (permitting multiple states to exist side-by-side). Ideally you'd start with passing the record around, but sometimes you just write code to get the concept implemented and refactor from there.
- zozbot234 6y agoLanguage-enforced association with methods and functions makes it possible to have full encapsulation in a way that's guaranteed by the language itself. That's not even OO either, just simple object-based code. You even see these same ideas entering FP nowadays, with things like univalent types that enable one to establish equivalent types as "equal", and thus fully interchangeable, no matter how they're structured internally.
- Jtsummers 6y agoYes, this is true. I'm not arguing that procedural (in the C-style) is fantastic, but it can be scaled decently well, but requires discipline. There are definitely improvements to be had when you start adding other language features that enforce certain things.
- zozbot234 6y agoThe whole problem with "discipline" is that it's hard to write it down and enforce it in a large-scale project. Language-based mechanisms are inherently scalable (at least if properly designed; and arguably e.g. implementation inheritance isn't) and make it much easier to build software "in the large".
- walshemj 6y agoHow large a team I have certainly worked on large scale projects using procedural languages and I don't recall any problems with the required discipline
- walshemj 6y agoAnd having done a fair bit of procedural programming back in the day F77 and PL1/G only the most trivial of programs have every thing as global state.
- agumonkey 6y agoslight historical anecdote, back when graphical user interface were not a thing, sutherland team is said to have developped rendering in a fully streamed style. Relying on state too much is also a matter of context, that team didn't have memory really (at least nothing that could store a frame buffer) so they wrote imperative code in an iterable fashion.
- devericx 6y agoHow does functional programming allow you to avoid reasoning about state? Don't you always have to reason about state, no matter what paradigm you use? Otherwise, how are you going to build anything useful if you're not keeping track of any variables?
- AnimalMuppet 6y agoThe following words are bad: shared, mutable, global. All programs have state. It's worse if the state is shared. It's worse if the state is mutable. It's worse if the state is global. It's the worst if it's all three. Functional program still have state. They typically don't have shared state, or mutable state, or (much) global state, though. Those are real wins.
- Gibbon1 6y agoI feel like a lot of functional programming tries to hide state behind an OS/API call of some sort. Sometimes that makes sense. Think a program that transforms data into SQL calls. Mirror of that is a driver for a piece of hardware. You got multiple levels of state going on.
- kristopolous 6y agoThe formalism and structure IS the critique. If the tools become about the rules they hinder instead of help.
- _0ffh 6y agoYes, two points: 1. To try to force restrictions on someone who doesn't understand the reasons for these restrictions will not magically ensure good results. 2. To try to force restrictions on someone who does understand the reason for these restrictions is just a pointless nuisance.
- overgard 6y agoProcedural programming was never abandoned, it just doesn't have a hype machine around it. How much software is written in plain C which is neither OO nor functional?
- pmarreck 6y agoIt is perfectly possible to use a functional style in an OO/procedural language, as John Carmack demonstrates below. You just lose the guarantees (which is a steep cost IMHO) https://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php https://www.gamasutra.com/view/news/169296/Indepth_Functiona...
- zackees 6y agoOne of the main reasons why OOP is popular is not even talked about - the ability for AUTOCOMPLETE in the IDE to work extremely well. For example, I have an object... what do I do with it? So I hit the '.' on my keyboard and read the list of things that the object can do. This is why OOP is popular, it allows better tooling. If people want to move beyond OOP then they need to think about how the new style integrates with the IDE so that the programmer can hit the '.' or equivalent and get a list of useful things to do with the object in question.
- kqr 6y agoThis is a common idea but I'm skeptical it's that simple. Haskell has a powerful enough type system that it can enable "hole-driven development" where you specify the type of your code and then leave a "hole" in the implementation -- anywhere in the implementation, including making the entire implementation a hole! -- and the tools can suggest what should go in the hole, leaving new holes in the code. Then you can incrementally resolve smaller and smaller holes based on the suggestions you get, and finally you have a completely tool-written program, where you just helped disambiguate when you had to. This is the '.' on steroids. Haskell is not popular.
- vkou 6y ago
- badrabbit 6y agoDesign and framework are different things though. You can use oop design when needed without having the whole world evolve around it.
- atoav 6y agoAs somebody who tried to force OOP onto Rust only to realize how easy things can go when you use a more data-oriented/compositional approach: I think the most important thing to keep in mind is that OOP is only one possible way of abstracting things. There are other ways as well and all of them work well for certain problems and not so well for others. Limiting yourself to only one lense with which you look at problems is a bad thing.
- commandlinefan 6y ago> Limiting yourself to only one lens Well, agree, but in my experience people who abandon OO are usually the ones who are limiting themselves to only one lens - the top-down/procedural way software was developed in the 60's and 70's which is easier to comprehend but ultimately unsustainable for subtle reasons. (not saying that's you, just saying a lot of people do). Rejecting OO in favor of FP like Paul Graham does? Great! Hopefully you'll make some headway and functional languages/styles will really catch on! Rejecting OO in favor of one big function that handles everything and a few global variables (like nearly everybody who uses the Spring "framework" abomination in Java)? Maybe spend some time considering why, if it's so obvious, it's referred to as a "bad practice".
- debug-desperado 6y agoWithout DI frameworks and IoC class design, good luck testing your OO code. I also don't get the Spring hate and what that has to do with creating "one big function and a few global variables."
- commandlinefan 6y agoDependency Injection and Inversion of Control are great design principles that most capable programmers discovered on their own before they were given names. Having a worthless container instantiate everything via reflection and moving what ought to be compile-time errors into the runtime to enforce that is an unnecessary waste.
- AnimalMuppet 6y agoOOP had another advantage over procedural: Often in procedural programming, you'd pass around a pointer to some data structure. If the data structure got messed up (put in an inconsistent state, say), you had no idea what code was responsible. With OOP, you look at the class's member functions, which is a much smaller set.
- zozbot234 6y ago> If the data structure got messed up (put in an inconsistent state, say), you had no idea what code was responsible. With OOP, you look at the class's member functions, which is a much smaller set. Yes this is correct. But data abstraction and encapsulation are found in all object-based code; they're not specific or related to OOP.
- AnimalMuppet 6y agoI'm not sure I understand. In your view, what is the difference between "object-based code" and OOP?
- kingdomcome50 6y agoSimply using objects in the code doesn't mean one is practicing OOP any more than using functions means one is practicing FP. That is, it is more about the "how" than the "what".
- beeforpork 6y agoHow is there a smaller set because of OOP? Just filter the functions by parameter type to restrict which function may access your pointer.
- Jtsummers 6y agoOOP encourages modifying an object's state via its member functions (if you use data hiding/encapsulation as part of your OOP definition). This means that users of class instances shouldn't be able to put it into a bad state, unless the member functions are able to put it in a bad state. This lets you isolate where your contract/invariant violations are more easily. Once addressed in the member functions, either it fixes it in the callers, or their use is now invalid and becomes a compile or runtime error.
- user234683 6y agoAs you demonstrate, there's not always a clear-cut distinction between OOP programs and non-OOP programs. Sure, the Linux kernel uses some OOP concepts, but it couldn't be described as fully OOP in aggregate. In my opinion, the best paradigm is no paradigm at all: use the right tool for the right job.
- munchbunny 6y ago"Use the right tool for the right job" is the final stage of "first, learn the rules, then learn when to break them." The difficulty with that advice is that it doesn't become applicable until you've gotten maybe 5 years worth of humble pies thrown in your face, for moments where you realize in retrospect that you made the wrong call. Until then you don't have enough of a body of experience to tell two very different scenarios apart: "things are this way because my predecessors already learned the hard lessons" vs. "things are this way because my predecessors didn't know better." Paradigms are really useful in practice as a shortcut to implementing 80% of the wisdom until you begin to learn the remaining 20%, and one of the roles of senior engineers is to make sure junior engineers don't mistake the 80% for the 20%.
- a1369209993 6y ago> "things are this way because my predecessors already learned the hard lessons" vs. "things are this way because my predecessors didn't know better." This is just Chesterton's Fence again, and has the same refutation: if you're putting up a fence that serves some long-term purpose, it is your responsibility to put signs on it explaining what that purpose is. Otherwise people are right to assume it's one of the overwhelming majority of fences that do not, in fact, serve any purpose beyond obstructing their movement.
- mdpopescu 6y agoSure, that makes a lot of sense, and it's a sensible demand when there are a million people, each putting up one fence. However, when you write the same architecture over and over, you don't write an explanation each time - for various reasons, a not insignificant one being that there's no obvious place to put it. (I use dependency injection in my code. Where should I explain why - on each class? In a separate file in the project? In the documentation? Should I write documentation, then? What if I write it and others continue to "new" services inside their code?)
- barrkel 6y agoProcedural code is quite popular in Java as used with IoC. Global variables are called "Singletons". DI injects a reference to all the globals you need to do your work, so you don't feel so grubby. Often DI injects some kind of accessor to the database, a giant store of global variables. Procedures are called beans, but you can tell they're procedures because they have a single public entry point and are usually called SomeKindOfVerber with a main verb() or do() or execute() method, they have the procedures they call (sorry, I mean "collaborators") injected, and they themselves are injected to where they'll be called from. Their state is ephemeral, and only exists for the purpose of executing a single flow of logic (this is called "bean scope"). All you need to do to describe your code as object-oriented is convert your procedures into beans, and tada!
- hagy 6y agoI agree with a lot of this, although I wouldn't conflate DI references with global variables. They certainly allow for shared state between different components, but the great thing about DI is that you can always create new contexts with a separate set of shared state references. I also agree that is kinda of funny to constantly so the Verber classes everywhere in Java, but it has at least improved over the last several years with the standardization around Function<T,R>, Supplier<T>, Consumer<T>, etc. Lambda syntax and method references also help. I'd admit the syntax for using a functional style in Java is still a little jenky, but its quite an improvement from where things were back in the day.
- oneplane 6y agoI agree and might add that having improvements on something imperfect (but a thing that does get real work done) can be worth much more than compsci perfectionism. It is very hard to do something that is 'good' in technical terms while also being 'good' in product terms. While the discussion often doesn't revolve around the practical implications of taking steps toward a commercially uncommon language or paradigm during business engineering it is pretty much where all the 'stuff gets done'.
- 6y ago
- jcelerier 6y ago> Even if you look at well-designed non-OO code like, say the Linux kernel but the linux kernel is definitely OO, except vtables are filled by hand instead than with the help of a compiler. https://www.kernel.org/doc/html/v4.10/driver-api/infrastructure.html https://www.kernel.org/doc/html/v4.10/driver-api/infrastruct... structs with data + function pointers are literally OOP objects. see e.g. https://lwn.net/Articles/444910/ https://lwn.net/Articles/444910/ > you'll see that there are object oriented concepts like information hiding and polymorphism all over the place; they just don't formalize them with the "class" or "private" keywords. yes, that's still OO. python doesn't have a private keyword either and that does not make its classes not OO.
- kragen 6y agoSmalltalk doesn't have a private keyword, or private methods, and it's the language the term "object-oriented" was invented to describe.
- btilly 6y agoyou can't realistically develop useful software in pure-procedural way without introducing a lot of global state. Even if you look at well-designed non-OO code like, say the Linux kernel, you'll see that there are object oriented concepts like information hiding and polymorphism all over the place; they just don't formalize them with the "class" or "private" keywords. You are misattributing that to OO programming. The original edition of Code Complete, written in 1993, makes absolutely no mention of OO programming or any OO principles. However it has a very good discussion of information hiding, and procedural code written in the various styles that it recommends do not create a lot of global state. (And yes, I have worked with such code.) One of the best things about OO was that it pushed programmers who had not absorbed best practices towards that. However OO encourages combining the principle of information hiding with OO notions of inheritance. Many learned the hard way to prefer composition over inheritance. (Many, unfortunately, never learned that. Just as many procedural programmers never learned about information hiding.)
- arc776 6y agoI feel like inheritance is the actual problem with OOP. Trees of types with increasing specialisations just don't describe many real world problems that well. When you do build programs like this, code gets more and more rigid until it becomes a maintenance nightmare, and extremely expensive to pivot functionality. Sometimes having a complex object hierarchy literally stops you making some new feature. A lot of mental effort is sucked up in mashing problem spaces into hierarchies, and once there the program is constrained by them.
- zozbot234 6y agoIt has little to do with subtyping per se and everything with the way that implementation inheritance, specifically, breaks modularity. The whole idea of inheritance contra composition is that every call to a virtual method-- including calls internal to the class-- goes through a dispatch step so that the actual code that gets executed depends on whether the object is one of a "derived" class, where that method might have been changed in ways that might break any amount of expected invariants. This introduces brittleness both in base class code (which generally has to call methods that might get overridden in derived classes) and in the derived class itself (which has no way of knowing what invariants might be expected of it as part of base class code). Implementation inheritance is often justified as a way of "reusing" code, but as it turns out, we've merely introduced undesired coupling instead of the seamless reuse we might have expected.
- throwaway894345 6y agoThese conversations aren't helped by the fact that OOP isn't well-defined, and observations about 'typical' OOP--the banana that has a reference to the gorilla and transitively the entire jungle, or the pervasive abuse of inheritance--are "aren't really OOP and you can write bad code in any paradigm!" even though other paradigms (for whatever reason) don't share these issues at nearly the scale as OOP. Inheritance and god objects were certainly how OOP was taught at universities and in the predominate literature when I was in school circa 2010. And if these (mis)features aren't part of the definition of OOP, then what distinguishes it from functional and/or data oriented paradigms? Encapsulation? Seems like FP has encapsulation and DO is orthogonal. Certainly localization of state is common to both FP and DO--it's not an OOP innovation. If OOP is so abstract as to be indistinguishable from other paradigms, then what value does it add?
- ipnon 6y agoAn object is in some ways a function that defines its own parameters (fields and methods) and arguments (state). This then is injected into another such function.
- uryga 6y agoif it's a function, what are the inputs/outputs? do you mean that in the way of "an object is a poor person's closure"? in case anyone's unfamiliar, here's one basic way to emulate objects with closures. i think i saw it in SICP (?), reproducing it here because i just love how simple it is: // might be easier to first look at // the usage example at the bottom let Point = (x, y) => { let self = (message, args=null) => { switch (message) { // getters case 'x': return x; case 'y': return y; // some operations // (immutable, but that's not required) case 'toString': return `Point(x=${x}, y=${y})`; case 'move': let [dx, dy] = args; // use our getters return Point(self('x')+dx, self('y')+dy); // let's get DRY! case 'plus': let [other] = args; return self('move', other('x'), other('y')); default: throw Error(`unknown message: ${message} ${JSON.stringify(args)}`); } }; return self; }; let p1 = Point(3, 5); // sending messages p1('x') === 3; p1('y') === 5; p1('move', [1, 2])('toString'); // --> "Point(x=4, y=7)" let p2 = Point(1, 2); p1('plus', [p2])('toString'); // --> "Point(x=4, y=7)" dynamic dispatch & message passing, just like that! and you can easily do `__getattr__/method_missing`-style dynamicism just by looking at `message`. for the other way around, see how Java lambdas desugar to objects with a "call" method and closed-over variables as members.
- _0ffh 6y ago>object oriented concepts like information hiding Having read "Software Fundamentals" [1], I think David Parnas [2] would beg to differ on that bit. Not the least because he actually invented the concept. Parnas wrote extensively on information hiding as the basis for modular program construction, abstract interfaces that provide services without revealing implementation, and other software engineering topics. [1] https://www.goodreads.com/book/show/1416932.Software_Fundamentals https://www.goodreads.com/book/show/1416932.Software_Fundame... [2] https://en.wikipedia.org/wiki/David_Parnas https://en.wikipedia.org/wiki/David_Parnas
- cy_hauser 6y agoNot sure what you're saying. Are you saying OOP does not have information hiding or that the "information hiding" in OO languages should not be called information hiding as per Parnas? Or something else entirely?
- revertts 6y agoI read their point as “information hiding is not a concept exclusive to OO.” Whether or not the original quote meant to imply that I’m not sure, but do see how it could be read that way.
- _0ffh 6y agoI'm saying that just because someone learned about a concept (in this case, information hiding) in a certain context (OOP), that does not mean that this is where that concept originated (which was implied). It's a common mistake I see all the time. In short, information hiding is a concept from modular procedural programming that was then adopted by OOP, not an original OOP concept. In general, OOP is really mostly a case of "let's take a bunch of known best practices and encourage (or even enforce) them on the language level". Procedural programming can be as object-oriented (or not) as you like. With minimal amounts of syntactic sugar in the compiler, like e.g. rewriting "foo.bar(...)" to "bar(foo,...)" you won't even be able to easily tell the difference when you see the source code. While I'm at it, the critique that procedural programming necessarily results in a quagmire of global state is also wrong. It's all up to the programmer. Nobody forces you to use globals. Some OO languages try to force you not to, but usually fail in the face of determined opposition from the coder. In essence, foo coders will find a way to produce foo code.
- michaelbrave 6y agoI don't know much bout procedural programming, but I'm curious about your thoughts on swift protocols and also how swift allows classes to be used in a functional way. I wonder if this could mitigate some of those problems?
- bklaasen 6y agoI think a mention of Gary Bernhardt's "functional core, imperative shell" talk from 2012 is worthwhile here. My mind was blown when I first saw it. https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell https://www.destroyallsoftware.com/screencasts/catalog/funct...
- scottmp10 6y agoI was introduced through his "Boundaries" talk [1] and had a very similar experience. I think a lot of programmers develop this methodology without really formalizing it. But practicing it consistently creates really nice code and easy unit testing. The one possible downside is long argument lists to functions. [1] https://www.destroyallsoftware.com/talks/boundaries https://www.destroyallsoftware.com/talks/boundaries
- lkrubner 6y agoHmmm, I have to push back against that somewhat. Do you have specific examples? I am mostly known for arguing against OOP and for Functional, and when I was doing my research most of the essays I read were aware of the Functional paradigm. Please see here: http://www.smashcompany.com/technology/object-oriented-programming-is-an-expensive-disaster-which-must-end http://www.smashcompany.com/technology/object-oriented-progr...
- it 6y agoHidden information is entropy. Do you really want more of that in your system? https://physics.stackexchange.com/questions/29175/why-is-information-indestructible https://physics.stackexchange.com/questions/29175/why-is-inf...
- golergka 6y ago> Usually when I see somebody arguing against object-oriented programming, I don't see them arguing for functional programming, but instead arguing for procedural (like Cobol, Basic, or Pascal) programming. That's not what I'm seeing. The usual discourse around OOP is that some parts of it are bad. In particular, overriding concrete methods, and in general Java-like inheritance brings a lot of pain. "Everything is an object" from C# and Java also gets a lot of well-deserved criticism (https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...). Other parts, like polymorphism, incapsulation, binding data with behaviour - I don't see that much criticism against this. For me, modern languages like Go or Rust (yes, I know it's almost a meme at this point, but still) bring sane OOP approach: while not being strictly OOP, they take the parts that work, and remove the parts that don't, while at the same time adopting many practices and abstractions from functional languages that proved useful.
- _bxg1 6y agoPersonally I've lumped procedural and OOP together into a single concept. Put differently, I consider OOP as a strict successor to procedural. Both are about side-effects and state, OOP just gives you smarter tools for encapsulating and managing that state. FP lets you eliminate some state altogether. My personal philosophy is to eliminate as much state as you can, and then to smartly manage what's left.
- jbverschoor 6y agoState is simply scoped more often. It’s all just a bunch of functions and memory
- slifin 6y agoThere's a good talk here from Uncle Bob who helped popularise the solid principles, about what OOP is, and isn't https://youtu.be/QHnLmvDxGTY https://youtu.be/QHnLmvDxGTY Towards the end of the talk note of the stickers on his laptop
- pacala 6y agoThere are 4 mainstream programming styles: * Data Oriented Programming. There is data, and there are functions operating on the data. * Functional Programming. Variant of DOP, with a strong emphasis on purism and abstract algebra concepts. * Procedural Programming. Variant of DOP, lacking language level mechanisms or best practices around polymorphic interfaces. Largely obsolete. * Object Oriented Programming. Everything is an object. Information hiding and polymorphism are readily available in all styles, except PP. The trouble with OOP is that insists on two big lies: * Everything is an object, therefore data is an object, therefore data must be hidden. In fact, data simply 'is' and you can't hide it. Think JSON. There is nothing to hide. * Everything is a polymorphic object, codified as the popular 'Open/Closed Principle'. In fact, most code is functions that just compute some data. Never needs N different polymorphic variants. If the business cases ever warrant such variants, simply refactor a polymorphic interface into the code base.
- dingoegret 6y agoI don't see any difference between DOP and procedural. Saying the difference is best practices and something vague about language mechanisms is accute to saying procedural is different to DOP because people say "do it good". This is even more formless than the criticism of no hard definitions levied against OOP by the same people.
- pacala 6y agoThe technical difference is lack of language-level module support, requiring manual management of vtables. It can be done, see Linux kernel for an example, but often times degenerates in a ball of globals. To further clarify, one can use DOP style in many languages, including languages that are ostensibly OO. DOP is a style, using data hiding and polymorphism as sparingly as possible, as opposed to OOP style, which strongly encourages using data hiding and polymorphism at every corner.
- dingoegret 6y agoOkay so it just sounds to me like careful OOP. Discipline just isn't a paradigm. It's an ethic and it's a good ethic. I love OOP, I've always built systems I was proud of with it. But to do that effectively you do need discipline and you need experience in design. Which most people think they can just skirt blindly by writing procedural code in class wrappers and the system will just magically emerge. This results in even more horrific programs when combined with blind TDD. This has always been nonsense. I don't use much OOP nowadays due to my work prioritizing efficiency and speed. Which OOP fails at miserably. But every now and again I'll find myself doing some monomorphism that looks like vtable dispatch without ever touching heap. And I'll kindly look back on my OOP days.
- ferzul 6y agosimilarly, there's elm/redux style state management, which is almost explicitly about emulating global state in a functional architecture. it seems all programming paradigms end up devolving into global state by friday. because too many developers cannot recognise global state, instead recognising only its syntax
- namelosw 6y agoPlain global state is not that bad actually. What people do to fix the global state made things worse, and IMO OO is also a terrible attempt. Mutate coarse-grained or global state is pretty much similar to functional programming approaches. The global state is a big tree, and there are functions designed for this type, there are also functions and types for every fraction of the tree. It's straight forward, elegant, and extensible. It's basically what Alan Perlis said in SICP: "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." The only difference is just functional programming always transact the whole tree instead of mutating it, since they use immutable algebra data type. It's not far from what Redux/Erlang process/Haskell ST Monad etc does. IMO OO makes it more obscure and too subjective compared to this approach. It makes people lost the whole picture very quickly, and then accidental complexity comes in play in every corner.
- bernawil 6y agobingo. It's not "global state" what's wrong. It's global MUTABLE state. OOP brought the terrible idea of coupling state and behavior, so of course "global state" would be wrong.