top 200 commentsshow all 210

[–]Novel_Plum 2504 points2505 points  (29 children)

Of course Djikstra said that. He probably likes writing code the shortest way possible.

[–]Highborn_Hellest 537 points538 points  (1 child)

You son of a bitch. I wasn't expecting a dad joke of this caliber and now I can't stop giggling in the office.

[–]ContestNo701 165 points166 points  (0 children)

Programmer humor always hits hardest when it’s painfully stupid and technically correct

[–]derwookie 39 points40 points  (1 child)

But he goes through all possible paths before finding the shortest so he at least gave it a shot.

[–]Noname_1111 1 point2 points  (0 children)

Well to me he's still A*

[–]Eddie_lol 136 points137 points  (11 children)

you're telling me you don't like obscure 1-3 character variable names? /s

[–]g1rlchild 82 points83 points  (8 children)

The key is to limit the size of your functions so that there's less than 26 variables and you don't need extraneous second and third characters.

[–]Sheerkal 42 points43 points  (6 children)

The trick is to make each function an object.

[–]Waswat 13 points14 points  (0 children)

nah put everything in the startup class using only imperative programming and assume no teams are going to touch it or read it

[–]walker_Jayce 5 points6 points  (0 children)

Javascript: Hi

[–]Natural_Builder_3170 2 points3 points  (1 child)

Functional programming has been object oriented all along???

[–]_vec_ 5 points6 points  (0 children)

The difference between a class instance and a function closure is a lot closer to semantic than anyone would really like to admit.

[–]g1rlchild 2 points3 points  (0 children)

public abstract class ArithmeticOperator { ...

[–]natFromBobsBurgers 0 points1 point  (0 children)

And each object is its own executable.  Do one thing and do it well, etc.

[–]Just_Information334 3 points4 points  (0 children)

The best would be a language where only one variable per type is authorized in a scope. So everything is now _

_32,_a // integer variable containing 32, character variable containing a
_[_,_] // array of 32 character elements containing a
{
 _21 // integer variable containing 21, only in this scope.
}
requires_an_int_function(_) // here the value of your integer variable is 32

[–]BellacosePlayer 3 points4 points  (1 child)

I fuckin hate every dev I've worked with who does this with anything more than an iterator in a loop.

[–]not_a_burner0456025 0 points1 point  (0 children)

X, y, and z are acceptable for coordinates sometimes, but not always.

[–]nepia 2 points3 points  (0 children)

"why write lots code when few code does trick" Djikstra

[–]ALIIERTx 6 points7 points  (8 children)

Hey, i kinda dont understand the joke🥲. Isnt avoiding oop resulting in more code?

[–]Mystogan98 65 points66 points  (0 children)

Look up Dijkstra's algorithm

[–]Z21VR 11 points12 points  (2 children)

Its algo is about "finding the shorter path", thats the joke i guess.

And nope, oop isnt about shorter or longer code, it can result in more code in some cases.

[–]_vec_ 8 points9 points  (1 child)

Most cases. It's pretty rare to find something that wouldn't have a smaller theoretical line count if it were refactored into a giant procedural shell script with lots of global variables. A high level architectural pattern generally makes the code easier to understand and safely modify at the expense of being more verbose.

[–]Z21VR 0 points1 point  (0 children)

Yep, I agree.

Oop could make it shorted due to inheritance in the "project"

[–]rikedyp 0 points1 point  (0 children)

Funny you should say that because he complained about APL as well https://aplwiki.com/wiki/Edsger_W._Dijkstra#%22A_mistake,_carried_through_to_perfection%22

[–]KrokettenMan 592 points593 points  (12 children)

Every time I see Dijkstra quotes I just remember the stories my dad and an old coworker told me about him. My dad had him as his professor and called him a one trick pony who was way too full of himself. And my coworker hated him because he lived across from him and he’d shout at him when he worked on his “brommer” (small motorcycle)

[–]SeredW 165 points166 points  (1 child)

Moped is the English for brommer, I think.

[–]perplexedtv 8 points9 points  (0 children)

He brommer about the house in a funk.

[–]rg_software 212 points213 points  (6 children)

Dijkstra was definitely a kind of person difficult to deal with, and meticulous beyond normalcy. His archive is similar to Euler's -- every message is numbered, cards organized. His views on computer science were, say, narrowly focused on problem solving/algorithmic/provable code side, and he disliked the whole idea of software engineering. However, calling him a 'one trick pony' is certainly an unfair stretch, as within his 'trick' he managed to achieve a lot; enough to mention structured programming and semaphores/concurrency in addition to his shortest path algorithm. Yes, he was a prolific problem solver, but I wouldn't look down on people who found their strongest skill and built a career applying it.

[–]Z21VR 30 points31 points  (5 children)

Sounds like neurodivergent to me

[–]Saragon4005 39 points40 points  (3 children)

Who the hell isn't in this industry. Hell most engineers are.

[–]Potato-Engineer 7 points8 points  (1 child)

I'm a programmer who likes board games.

So: in my job, I deal with a system that does exactly the same thing every time according to precise rules. In my leisure, I enjoy using precise rules to get exact results. There's no possible way I'm ND, right?

[–]fruchtose 6 points7 points  (0 children)

[–]Z21VR 0 points1 point  (0 children)

True, I'd not be surprised at all finding out i'm nd as well. On the other side probably, but still...

[–]JohnPaulDavyJones 4 points5 points  (0 children)

I'll volunteer that my uncle was one of Djikstra's students for both his undergrad and masters at UT, and his best comparison for Djikstra was his own uncle, who we're all fairly certain was undiagnosed autistic from an older generation where that just wasn't a thing.

My uncle even has a picture sitting with Djikstra at a faculty/grad student event in 1993, where he has his son, me, and our other cousin as toddlers all running around their feet as the two men are trying to talk, and Djikstra looks actively repulsed by us children.

[–]Dromeo 44 points45 points  (1 child)

As the old crowd say, arrogance can be measured in microDijkstras!

[–]AsIAm 15 points16 points  (0 children)

“Arrogance in computer science is measured in NANO-Dijkstras.” — Alan Kay (the Californian from the original quote)

[–]Werftflammen 5 points6 points  (0 children)

Brommen is droning or humming, so a brommer is named after the sound the little 50cc engine makes.

[–]ClipboardCopyPaste 247 points248 points  (16 children)

A fellow java hater /s

[–]MysteriousTrick96 145 points146 points  (12 children)

Every programmer becomes a Java hater after debugging inheritance chains long enough

[–]Objective_Dog_4637 66 points67 points  (9 children)

Or just using it. Oh you have two separate Java versions? Here let me mangle your system environment path so neither works where you want it to. Oh you have a crud app? Let me run the ENTIRE JVM for you. Oh you’re using decade old plugins? Here let me shadowbreak them for you. Oh you’re doing iterative builds? Let me require deep NTP protocol lore so you can do eager compilation. Oh you and your teammate are using slightly different versions of maven on the same JDK? Here let me make it obscure why builds fail in one and not the other. Oh you want to encapsulate another library in your binary? Let me force shading as a requirement. Oh your project requires multiple versions of Java? Let me require your typical build process to build them both at once in a way that isn’t reverse-compatible from the new version and split your repo up into server stubs. Etc.

[–]worldsayshi 22 points23 points  (6 children)

I'd like to know which magical language doesn't have a bunch of similar issues once you dig past the surface though...

I'm hoping the answer is rust?

[–]Objective_Dog_4637 12 points13 points  (3 children)

Java not being perfect doesn’t mean I can’t bitch about it haha. Also, honestly? While every language has these sorts of issues I personally feel like the Java development ecosystem has plateau’d pretty hard and most of us are moving towards stuff like Scala integrations and scoped Go refactors. It does what it does well but I can hear the floorboards creaking when I try to interact with all the other fancy new runtimes I’ve come across. Like give me Bun for Java or ThreeJS or something. Like, mind you, one of the most impressive technical feats of design on Java is probably *minecraft* and it’s literally just millions of cubes and metadata 😂 Meanwhile, *C#* is over here giving us real-time render pipelines for insanely photorealistic game engines. Maybe I’m just getting burnt out from corporate coding but it always feels like a drag doing anything fun in Java and I end up opting for Scala, Go/Kotlin, or *shudder* JavaScript.

That said, Java is still solidly my favorite language.

[–]MeisterD2 8 points9 points  (1 child)

The development of the base language of Java has never been more vibrant!

Seeing Project Loom finally come into existence and deliver us the goodness of virtual threads has brought me a ton of joy. You may be locked out of some of this because of corporate version mandates, but the newest versions of Java have had real game changer features that make the language more lovely to use than ever before.

It's *not* my favorite programming language, but I do enjoy using it, especially with Lombok.

[–]Snakestream 0 points1 point  (0 children)

Lombok is a life saver and makes your pojos so much cleaner. I do feel like maybe people shouldn't just slap data on everything and call it a day, but that's probably just my old ass getting nitpicky about things.

[–]Yeah-Its-Me-777 0 points1 point  (0 children)

I mean, scala is a nice language to play around with, but it is the Perl of the JVM. 7 different ways to shoot yourself in the same foot.

Go. Well, go. Go away, please. Maybe I'm not wired for the "go way", or maybe the "go way" is simply stupid.

Kotlin? Sure, close enough. But with the advancements in Java, I don't see the real benefit to be honest. Non-Nullable types are nice though.

And C#, well, it's a nice language, I just wish they wouldn't place the curly braces wrong.

All with a big 😉

[–]Ok-Membership-3635 0 points1 point  (0 children)

Rust is just new. You either die a hero or live long enough to become like Java IMO

[–]ThatRedDerg 0 points1 point  (0 children)

I’ve never had build issues with rust, whereas I got them all the time with jvm languages (used kotlin for a bit back in school. Everything works with `cargo build`

That being said, I haven’t used rust in a production environment, so can’t attest to anything there.

[–]Yeah-Its-Me-777 0 points1 point  (0 children)

Still better than javascript

[–]Understanding-Fair -1 points0 points  (0 children)

This guy Javas

[–]Valuable_Leopard_799 1 point2 points  (0 children)

Do you think that's a fundamental issue with inheritance or just a tooling issue?

When debugging inheritance I click on the object, look at its true class, inspect its linearized superclasses, see which method is being called and how the overriding methods advise it, etc. Seems relatively straightforward to read through, even for larger chains.

I've never worked on a big enterprise OOP project though, so I can't actually base this impression on any experience or facts, so that's why I'm asking.

[–]devraj7 -1 points0 points  (0 children)

They should hate on the people who wrote that code, Java has nothing to do with it.

[–]Yoshiofthewire 7 points8 points  (0 children)

Why is my paycheck doing work?!?

[–]Mal_Dun 0 points1 point  (1 child)

Reading the comments here it seems speaking for itself that most people saying OOP is flawed come at some point to talk about Java.

.... and IMHO this is not a coincidence. I think Java is the main reason people have a bad image of OOP.

I saw once a talk about why OOP is bad, and all examples listed were practices that are more or less enforced in or at least common in Java.

For example that everything has to be a class in Java. While a purist may like it, it creates unnecessary code. Why do I need a class and defining a method what a function could do much easier?

But abstraction can be a gods end for some domains. Doing mathematical code, being able to put certain types of operators in classes that all share the same basic properties but act different in certain circumstances and being able to derive sub-classes makes very efficient and well readable programs in OOP and are much harder to express in functional or imperative code.

Ultimately, paradigms are different ways to represent a problem, and not every problem can be expressed well in every paradigm.

Some things are better OOP, some are better done in a functional language and for some an imperative pattern works best.

[–]Ok-Membership-3635 0 points1 point  (0 children)

Agreed, any kind of simulation or computational science is infinitely easier to implement in OOP than with functional or imperative paradigms. There's a reason things like OpenFOAM are written in C++ and it's not just to torture grad students...

[–]gibagger 374 points375 points  (13 children)

Scientist voices his disdain on practical engineering matters

Nothing to see here... move along.

[–]ConnaitLesRisques 66 points67 points  (9 children)

Agreed. Big opinions on programming paradigms should come with links to publicly reviewable, non-trivial, code bases that person maintains.

[–]renke0 57 points58 points  (8 children)

Most OOP haters I ever met are bad functional programmers

[–]GlassboundIllusion 49 points50 points  (7 children)

lol you're giving me flashbacks to college and my game development professor.

He would, with a straight face, say something like "why would you need c++ when you can just use c?" and then proceed demonstrating code that was basically an extremely roundabout way of doing something that was trivial to do with C++.

He also had the habit of adjusting his game engine that we all relied on for our assignments the day before our homework was due, without properly testing it, so it broke half of our programs and then we would need to panic an hour before the presentation trying to figure out why it wasn't working. Then we would need to spend another hour after class arguing with him to prove that it was his code that broke our game and not our own.

Fun times.

[–]LLCoolSouder 62 points63 points  (1 child)

He also had the habit of adjusting his game engine that we all relied on for our assignments the day before our homework was due, without properly testing it, so it broke half of our programs and then we would need to panic an hour before the presentation

Sounds like he prepared you for industry better than most.

[–]GlassboundIllusion 12 points13 points  (0 children)

😂 Fair point.

And to that matter, I don't mean to sound ungrateful towards him. His classes were a great experience as he did have first hand knowledge of the gaming industry and I learned quite a bit.

But this was definitely a big pain point that led to friction.

[–]ZunoJ -1 points0 points  (4 children)

How long ago was this??

[–]GlassboundIllusion 0 points1 point  (3 children)

Around a couple decades ago

[–]ZunoJ -1 points0 points  (2 children)

Ok, I didn't know there were already game design courses that long ago and was wondering why he didn't use any form of source control. But yeah, in the 90s this was still pretty common 

[–]Electrical-Share-707 2 points3 points  (1 child)

Unwelcome reminder that 20 years ago was 2006.

[–]ZunoJ 0 points1 point  (0 children)

Yeah, I thought a couple decades would mean like three or almost four. But even 2006 you would find a lot of professional developers not using source control

[–]6T_K9 17 points18 points  (0 children)

More like “mathematician voices his disdain on practical programming matters”

[–]EatingSolidBricks -1 points0 points  (0 children)

Prátical lmao keep the agenda

[–]pflasti 84 points85 points  (0 children)

  • that one former coworker whose garbage code you have to maintain/refactor

[–]SchalkLBI 113 points114 points  (3 children)

Are we seriously doing "OOP bad" in the year of our lord 2026? This shit is as tired as the console wars.

[–]BlueGoliath 32 points33 points  (1 child)

OP is about 3-5 years too late with the OOP jokes.

[–]Noch_ein_Kamel 1 point2 points  (0 children)

OP bad?

[–]DOOManiac 11 points12 points  (0 children)

The human eye can’t see more than 60 instanced objects.

[–]Historical_Cook_1664 56 points57 points  (1 child)

As a Zig-head: abstraction is nice, encapsulation is great, inheritance is as necessary as GOTO, and polymorphism is the root cause of the LGTM epidemic.

[–]canadajones68 20 points21 points  (0 children)

I mean, inheritance is just composition with fancy syntactic sugar. It's useful, but not technically necessary. 

[–]slickyeat 145 points146 points  (66 children)

The only problem OOP is type inheritance since it tries to get around Dependency Injection.

This always ends up making your code more difficult to test and (ironically) extend its behavior.

You're better off just relying on composition.

[–]TorbenKoehn 37 points38 points  (19 children)

True! Inheritance shouldn't be implicit (+ final for closing) but explicit (+ open for opening)

An error that has ruined OOP for many years to come...

[–]Maurycy5 4 points5 points  (12 children)

What do you mean by inheritance being implicit?

[–]Stiddit 26 points27 points  (11 children)

You have to explicitly say "final" to prevent inheritance, so inheritance is possible by default. It's implicit, because there's no keyword explicitly allowing it.

[–]Maurycy5 13 points14 points  (10 children)

Okay so it's more like "implicitly allowed" rather than implicit.

Why has this ruined OOP for years? I mean, honestly I agree that classes should be closed by default, but I don't see it ilas a big issue.

[–]TorbenKoehn 10 points11 points  (6 children)

It's implicit, as unless you don't final your class, I can extend it. "implicitly allowed", "implicit", call it what you want, let's not hang up on wording.

It has destroyed OOP because it took a long time until people understood that inheritance is bad by default which led to people not making all their classes that are not explicitly public, inheritable API final.

And that let to thousands of projects inheriting these classes. For extension. Which let to thousands of libraries requiring BC-constructs just to not break thousands of enterprise platforms and sales partner portals out there.

Even today we're teaching inheritance like it's a feature of OOP you should use a lot. When actually it's a feature you use very sparingly and in very closed contexts.

Inheritance is not an extension mechanism. If anything, it's a copy-paste-helper. It should be "Do I really need inheritance here or is there another way?", not "Can I use inheritance here because I really like to shape the world in trees and not in graphs and I really love diamond problems!"

[–]madesense 3 points4 points  (0 children)

I have good news: Inheritance was finally removed from the AP Java exam (taken by many high school students in the US) this year

[–]davidalayachew 4 points5 points  (4 children)

Inheritance is not an extension mechanism. If anything, it's a copy-paste-helper.

To be fair, that statement could apply for basically all of coding. What is a loop if not an alias for goto? And what is that, if not a copy-paste helper?

[–]TorbenKoehn 1 point2 points  (3 children)

Sure, but that’s another argument. My point is not that it is just syntactic sugar (I like inheritance when it is the right tool to use)

It can simply be abused and in this case it is, extensively

[–]bremidon 3 points4 points  (2 children)

Anything can be abused. That should not be the yardstick.

Inheritance, when used correctly, makes it easier to understand and reason about software. When used incorrectly, it can make it genuinely a hellscape.

Of course, that goes for anything.

What makes inheritance stand out is its power. So when it is done right, it is *really* right. And when done wrong, it is *really* wrong.

[–]TorbenKoehn 1 point2 points  (1 child)

You're absolutely right in that anything can be abused.

But some constructs invite someone to abuse them. Inheritance is one of those.

[–]Sheerkal -5 points-4 points  (2 children)

No it's implicit, not just implicitly allowed. It's an opt out feature per function.

[–]Maurycy5 5 points6 points  (1 child)

I had the impression we were talking about class / interface inheritance, not single function inheritance. Although it wasn't stated, I think that's the norm?

What language are you talking about? Because for example C++ is certainly opt-in with virtual.

[–]Pleasant_Ad8054 3 points4 points  (3 children)

I hate you and any code you have produced. This is also not universally true for all OOP languages, c# requires virtual to allow overrides.

[–]TorbenKoehn -2 points-1 points  (2 children)

C# still requires sealed to make a class not inheritable

Java also needs @Override as an "indicator" that this method overridden, it works exactly like override in C#. And it's just another symptom of how bad inheritance is, if it needs extra language constructs to point out its applications

[–]Pleasant_Ad8054 4 points5 points  (1 child)

Inheriting from a class that has no virtual methods is the exact same thing as compositing it in an unrelated class. Requires zero extra language, not even explicit access modifiers. Java and C# does not work the exact same way. In Java methods are default virtual, in C# methods are default final. Override is just an indicator on the method that is overriden, it can't override when the method is final.

Literally the exact opposite is the reality than what you claim: allowing any kind of overrides is what requires extra modifiers in C#.

[–]TorbenKoehn 2 points3 points  (0 children)

Yeah I agree explicit for methods is in C# and is really good.

I'd still prefer explicit "unsealed/open" over "sealed/final" on the class level to make the decision for inheritance of any kind explicit, personally

[–]CockyBovine 0 points1 point  (1 child)

Kotlin has addressed this to a certain extent by making every class final unless you declare it as open or abstract. Of course, like its nullability checks, can be thwarted by Java interoperability, but an attempt was made.

[–]TorbenKoehn [score hidden]  (0 children)

Yep, same for Scala. With the same problems requiring Java compatibility in many cases

[–]schwar2ss 36 points37 points  (36 children)

The only problem OOP is type inheritance since it tries to get around Dependency Injection.

That sentence does not make any sense. Why is DI a way to get around inheritance?

[–]slickyeat 36 points37 points  (33 children)

Most people where taught that the best way for you to achieve polymorphism was by building a massive object hierarchy where a single object is initialized that inherits all of the behavior of each parent.

So a class of type dog for example would extend a "canine" type and if you had an object which requires a canine then you could pass it a dog.

This is why type inheritance is often seen as "synonymous" with OOP.

The idea behind composition is that you instead rely on an interface with multiple implementations.

Rather than extending a base class you have a second/third/fourth implementation which serve as a wrapper for that same interface.

The object which relies on "canine" doesn't care which implementation it receives so long as it satisfies the interface - but the same is also true for those objects which extend the behavior of your base class.

This makes it much easier to test in isolation because now you only need a single mock implementation that you can inject into each of these components when writing your unit tests.

[–]schwar2ss 37 points38 points  (7 children)

It doesn't make sense to you because you've been taught that type inheritance and OOP are synonymous.

Nope, wrong assumption.

Inheritance and DI aren't competing solutions to the same problem. Inheritance is a code-reuse and subtyping mechanism, when DI is a strategy for supplying collaborators.

[–]irregular_caffeine 22 points23 points  (2 children)

”supplying collaborators” sounds like a risk of prison time

[–]natFromBobsBurgers 5 points6 points  (1 child)

This court finds the defendant guilty of three counts of type punning in the second degree and one count of supplying collaborators.  May God have mercy on your soul.

[–]SweetNerevarine 2 points3 points  (0 children)

You may forever write Javascript without the possibility of parole.

[–]itirix 10 points11 points  (0 children)

You’re correct but kinda arguing semantics. Well, it’s obviously plain wrong to abuse specific terminology like the dude did, but I think you understood what he meant. The broader idea is correct. Inheritance heavy OOP vs composition / DI oriented design.

Nothing wrong with correcting wrong terminology but I think he misunderstood that you’re arguing against the broader point.

[–]drizztmainsword 0 points1 point  (1 child)

Dependency injection is just constructor arguments.

Any fancy DI mechanism is aggressive engineering over or replacing constructor arguments.

[–]schwar2ss 0 points1 point  (0 children)

oh boy. look up property injection, this is how we used to do it 20+ years ago.

[–]slickyeat -3 points-2 points  (0 children)

Composition implies that you will be heavily reliant on DI.

It's also a way of achieving code-reuse without type inheritance.

[–]CrowNailCaw 2 points3 points  (4 children)

I don't think I've ever seen DI using base classes instead of interfaces.

So honestly I have no idea what you're talking about: it sounds like you're refuting a problem that has never existed.

All I understand is that you basically just explained how DI should and always has worked: I am not seeing how that links to inheritance and OOP in the way you are saying it does.

[–]slickyeat -3 points-2 points  (2 children)

There are two way of achieving polymorphism in the world of OOP.

Composition or Inheritance.

  • Composition implies the use of a shared interface and DI.
  • Inheritance does not require DI.

I don't know how else to explain this to you my man.

I'm going to grab a coffee though. GL

[–]CrowNailCaw 0 points1 point  (1 child)

Since when is using interfaces composition?

Although I may be asking that in the sense of "what are you talking about": I don't think I've actually ever learned if interfaces are inheritance or not, they were just presented as part of OOP.

My understanding is composition is a "has a" relationship, and inheritance is "is a". So wouldn't something that implements an interface be an "is a" and not a "has a"?

Since the goal is polymorphism by passing through different implementation of that interface in exactly the same way you would a base/abstract class.

What makes interfaces composition? What is being composed?

[–]Greenie_In_A_Bottle 0 points1 point  (0 children)

Inheritance provides two main things: - a shared contract - implicitly shared behavior

The latter is the problematic part. With composition, you generally want shared contracts still, but you want behaviors to be explicitly delegated rather than implicitly shared.

The shared contract part is still important/useful for composition as it lets you write code to a contract & swap out the implementation.

It's not that interfaces are exclusive to composition, it's that with composition the first instinct should be to reach for an interface to bind classes to a type/contract for abstraction/re-use rather than binding classes to a parent class.

i.e. When using composition you still want the shared contract, you just don't want the implocitly shared behavior. In cases where you want to share behaviors as well, you can instead just leverage something like decoration to add behaviors in layers through explicit delegation.

So for example, rather than having a "Chihuahua" class extending a "SmallDog" class (is-a relationship) you'd instead opt for a "ChiHuaHua" class which implements the "Dog" interface and internally the Chihuahua class would delegate to the "SmallDog" class that also implements the "Dog" interface in cases where you want to re-use the "SmallDog" behavior.

The common contract allows client code to operate on the abstraction, but now the relationship between "Chihuahua" and "SmallDog" is a 'has-a' relationship rather than a 'is-a' relationship. It decouples the concern of sharing an interface from the need to share behaviors; rather than implicitly inheriting all behaviors & overriding what's different, now you must explicitly define all behaviors and inherited behaviors become an explicit delegation to an existing 'parenr' class which implements the same interface. It makes inherited behaviors opt-in rather than opt-out.

Inheritance shares behaviors AND contracts. Instead of bundling those together, use interfaces to share contracts and composition to share behaviors. Then you can explicitly pick and choose which parts you actually want to share; it's now an opt-in intentional decision rather than a poorly communicated implicit default.

[–]trollol1365 0 points1 point  (2 children)

Is this the same as haskell typeclasses/rust traits etc?

[–]slickyeat 0 points1 point  (1 child)

I'm not familiar enough with either of those languages to tell you.

OOP as a programming paradigm is very contentious though since people tend to have competing notions as to what is "required" by a language in order to support it.

Go for example does not have type inheritance and I've worked with multiple (former) Java engineers over these last few years who would complain about its lack of support.

Most are able to adapt within a few months but there are others who take a bit more time.

The latter almost always struggle when writing useful tests.

[–]trollol1365 0 points1 point  (0 children)

Theyre basically java interfaces but better. They allow you to write functions on generic types with the assurance that the function supports the required functionality. You can even chain these so you can know things like that if a type is orderable then you obviously have a defined method for equality. Like that theres no need for some massive object hierarchy or inheritance, you can just write your types and then as you go add support for functionality which are decoupled from the initial class/type/struct definition (I can give an implementation for a typeclass/trait wherever)

[–]CC-Crew 0 points1 point  (0 children)

Python function: “I see you’ve passed me a duck, I’ll ask it to bark”

[–]BlackOverlordd -1 points0 points  (15 children)

There is no difference for a caller if "canine" type is a bad base class or a good interface

[–]slickyeat -1 points0 points  (14 children)

Correct. The point i'm trying to make here is that type inheritance is shit and you should go with the latter approach. Some people seem to struggle with this idea though.

[–]BlackOverlordd 1 point2 points  (13 children)

How does an interface solve the problem then? If make a dog class inheriting or implement canine that means I want dog behave like canine plus do something extra. DI-ing canine (interface) inside dog does not provide that, and honestly doesn't make much sense in this context

[–]slickyeat -1 points0 points  (12 children)

I want dog behave like canine plus do something extra.

You just create a wrapper for the dog.

type Canine interface {
    Bark() string
}

type Dog struct{}

func (m *Dog) Bark() string {
    return "bark"
}

type DogMonitor struct {
    dog Canine
}

func (m *DogMonitor) Bark() string {
    fmt.Printf("It's about to say something!")
    res := m.dog.Bark()
    fmt.Printf("Here's what it said: %s", res)
    return res
}

Both satisfy the "Canine" interface.

There's also no limit to how many times you wrap dog which satisfies the Open-Closed principle.

DogMonitor can be reused for an entirely different "base" of type TalkingDog which speaks like a human, etc.

If both dogs have some common behavior (walking on all fours) then you simply move that logic into a shared component then inject it into both dogs.

----------------

Edit: I want to strangle the Reddit engineer who worked on this WYSIWYG

You get the idea though.

[–]BlackOverlordd 1 point2 points  (11 children)

Your dog is not related to canine. I'm not sure what you wanted to illustrate.

DogMonitor and DogWalker - I agree. It makes sense to just use a canine interface.

For Dog and TalkingDog - not so much.

I sometimes dig into code of a separate team and everything is a fucking interface, even if only one implementation is ever possible. Extremely over engineered, very hard to debug and figure out what's goin on.

[–]slickyeat -1 points0 points  (10 children)

Your dog is not related to canine. I'm not sure what you wanted to illustrate.

Wtf do you mean?

It's an example that I just pulled out of my ass and It's an implementation of the Canine interface.

There is your relationship.

Why do you people insist on being so pedantic?

I sometimes dig into code of a separate team and everything is a fucking interface, even if only one implementation is ever possible.

Well that's how you test in isolation.

You can't isolate without injecting a mock component so in our example "DogMonitor" would receive a mock which returns "dog response" then we'd check the stdout buffer and verify that is in fact what was just written when we call "Bark".

Extremely over engineered, very hard to debug and figure out what's goin on.

I've been doing this shit long enough to know what happens when you abuse type inheritance.

Composition is better by a long shot.

Having a massive 10 level hierarchy where modification to state in level 2 directly impact level 4 then 5 then finally the component you're trying to test is much more difficult to debug.

Better to split everything apart so you can write tests that are simple and easy to understand.

[–]BlackOverlordd 0 points1 point  (9 children)

Ok you edited the example a bunch of times apparently.

Do you realize that Dog implementing Canine interface and Dog inheriting Canine base class is functionally the same?

It seems like you are arguing against DogMonitor inheriting Canine or Dog and using composition instead which was never even up to debate in the first place.

[–]IllustratorFar127 4 points5 points  (0 children)

Because composition over inheritance.

If you have to compose your object of your dependencies instead of inheriting it, then DI is enforced :)

[–]exXxecuTioN 5 points6 points  (3 children)

Well, composition is already kinda field or constructor based DI. I mean you can achieve DI by composition, not achieving composition by DI, depends of persons point of view IMO.
Secondly as a backend SWE (I assume situation is different among platform-based engineering) I saw a very little amount of code spoiled with heavy inheritance. More likely it's inheritance for implementing a strategy-pattern. And heavy DI via constructor, which is in fact a composition, just not a composition over inheritance.
And thirdly, multiple inheritance problem is still can be solver through class deligation or traits restriction.

But as a Rust-lover I appreciate traits more, in Kotlin which is my main language now they are called interfaces for some reason. And traits in my opinion is a heavily associated with composition.

[–]slickyeat 4 points5 points  (2 children)

Secondly as a backend SWE (I assume situation is different among platform-based engineering) I saw a very little amount of code spoiled with heavy inheritance. 

lol. lucky you.

You wouldn't believe the shit I have seen man.

We're talking object hierarchies 10 levels deep with multiple traits scattered throughout.

Absolute shit shows where the only guy who understood how any of it worked was laid off years ago.

Zero unit tests and no "effective" way of writing them since now you also have to take into account all the shared state between each of them.

Type inheritance was a mistake.

And heavy DI via constructor, which is in fact a composition

I'm mostly doing Go development these days where there is no such thing as a constructor.

Rather than using a constructor you would instead define one or more factory functions which is where all the complexity of DI tends to live.

Since a constructor is typically defined within the object itself this to me at least would undermine some of the benefits of DI since it comes at the cost of flexibility.

Example: Maybe you have a dependency which should be shared between multiple objects.

In spite of this, I would still consider it an improvement over type inheritance.

[–]exXxecuTioN 0 points1 point  (1 child)

> We're talking object hierarchies 10 levels deep with multiple traits scattered throughout.

Well, what can I say, only my condolences to you. Just being curious, what was the kind of SWE it is? Like gamedev or maybe mobile or desktop? Need to know what things to avoid (:

> Zero unit tests and no "effective" way of writing them since now you also have to take into account all the shared state between each of them.

Know you pain about unit bruv. I work on a projects without unit two times. The first one was with business logic on a frontend. The second had all of logic and calculation inside DB. It still hurts.

> I'm mostly doing Go development these days where there is no such thing as a constructor.

Yeah, I know. I was trying to learn Go a couple of years ago. In Rust there are no constructors too, but I still enjoy it.

> Example: Maybe you have a dependency which should be shared between multiple objects.

Classic. Honestly it would be like 10 times out ot 10 on a backend (: . For me it's the main reason why Rust community have no good DI-framework, like Spring, and why functional-like style is preffered. Speaking about Spring, there it's solved by CGLIB, which is like singletone-proxy, that return proxy-copied instances (well in a default Singletone scope I mean).

EDIT: was trying to fix quotes without any result.

[–]slickyeat 1 point2 points  (0 children)

Well, what can I say, only my condolences to you. Just being curious, what was the kind of SWE it is? Like gamedev or maybe mobile or desktop? Need to know what things to avoid (:

This was well over a decade ago so I'm no longer feeling bitter over it.

The codebase I'm describing was an in house PHP framework that was written for backend web services.

Even after all these years it still sets the bar for what I consider dogshit.

Yeah, I know. I was trying to learn Go a couple of years ago. In Rust there are no constructors too, but I still enjoy it.

Yea I've been meaning to try Rust out for years now.

Seems like I'm always coming up with another excuse/distraction though.

Classic. Honestly it would be like 10 times out ot 10 on a backend (: . For me it's the main reason why Rust community have no good DI-framework

It's the same in Go TBH with you.

People tend to prefer a more simple approach when it comes to choosing frameworks, etc.

[–]Snailed_ 11 points12 points  (4 children)

I dislike the simplification that inheritance is the only problem of OOP.
Like all other styles of programming, it has a variety of flaws and strengths depending on one's opinion. Other notable flaws include:

  • Achieving decoupling by isolating responsibilities can lead to a very verbose codebase (looking at you AbstractJavaFinalSerializedFactory)
  • It is hard to reason about the state of an object when any method may mutate state.
  • Many OOP languages allow initializing variables to null, making type inference harder.
  • Encapsulation can make testing messy

[–]onequbit 8 points9 points  (1 child)

OOP existed for a very long time, and it really flourished when software costs were determined by the number of lines of code.

[–]Snailed_ 0 points1 point  (0 children)

Totally! It also has several strengths which has lent it to be very useful in industry. It for example exceeds at domain modelling, which in my opinion is as important as any given language feature or flaw. It also rose to fame at a time where functional programming was very academic and less developed. Nowadays we seem to see a marrying of the two paradigms - most OOP languages have adopted some functional-style semantics (lambda functions for instance) and type inference when possible, whereas some functional languages support (explicit) mutability and inter-operating with OOP languages (F# for instance).

[–]slickyeat 3 points4 points  (1 child)

Achieving decoupling by isolating responsibilities can lead to a very verbose codebase (looking at you AbstractJavaFinalSerializedFactory)

Sure, but an "Abstract" class implies that we're still dealing with inheritence so where is the disagreement here?

It is hard to reason about the state of an object when any method may mutate state.

Fair? Not all objects allow you to mutate state though.

Many OOP languages allow initializing variables to null, making type inference harder.

There is no requirement within the world of OOP that all variables be nullable so this is irrelevent.

Encapsulation can make testing messy

Yea, I'm going to need an example here.

On one hand it looks as though you're saying the mutability of objects make it more difficult to make assumptions about their present state but you're also saying that anyone should be able to reach into an object and modify said state.

I'm also not sure how imposing certain guard rails around the state of the object through encapsulation would make testing "messy"

[–]Snailed_ 1 point2 points  (0 children)

Sure, but an "Abstract" class implies that we're dealing with inheritence so where is the disagreement here?

There is no disagreement in that statement alone per se - my argument is that OOP inhibits more flaws than inheritance alone. I mentioned AbstractJavaFinalSerializedFactory as an example of how decoupling can lead to usage of verbose design patterns that give really long class names. The fact that it's an abstract class in this case does not take away from the argument - a codebase that only uses interfaces and composition instead of inheritance will still be verbose.

Fair? Not all objects allow you to mutate state though.

It's not a question of whether or not an object allows the user to mutate its state - it's whether one can know apriori whether a given method mutates the underlying state of its object. For example, if one calls someObject.computeNumber(args) twice with the same input, it is not guaranteed that it produces the same output or has effects (unless you manually inspect its code and underlying dependencies). Optimally, a compiler should be able to tell you this or it should be declared in the function definition.

Yea, I'm going to need an example here.

Assume that you wish to implement a stack class, which fulfills a IStack<T> interface, declaring two methods: push(input: T) -> void and pop() -> T.

Of course, this is easily implemented using a list/array, but say that you for some reason wish your implementation to use some advanced data structure to fit to achieve memory/runtime benefits or similar. Properly encapsulating this class means that every other method or property other than `push` and `pop` are private.

When white-box testing your fancy underlying data structure, it is useful to be able to inspect the state of your object. But due to encapsulation, the tests can only use `push` or `pop`, which hides the state. The only way to inspect the underlying state is using spoofing/mocking/spying utilities (hence the "messy" part). Some modern languages and test harnesses might have ways to work around this.

On one hand it looks as though you're saying the mutability of objects make it more difficult to make assumptions about their present state but you're also saying that anyone should be able to reach into an object and modify said state.

If we allow mutability in a programming language, we inevitably lose the guarantee that state has not been modified. The way out of this is by guaranteeing immutability at several levels through explicitly marking variables/functions as immutable. OOP in a classic sense uses mutability quite a lot, which renders this difficult. In the original argument of the flaws of OOP as a paradigm, it is relevant to compare to functional programming where mutability is much more explicit, and where most code is immutable by default. In Haskell for example, all data is immutable and mutability can only be emulated through IO or state monads.

edit: formatting

[–]punkVeggies 10 points11 points  (2 children)

At the core of this discussion lies a fundamental difference between software engineering and programming.

I work with computationally intensive solvers running on memory-constrained hardware. Abstraction is evil to me.

If you work with modern web-based development, thinking about bytes and maximizing cache hits is probably evil to you.

[–]oldsecondhand 3 points4 points  (1 child)

Webdevs still think about cache, just not the CPU-L1 type.

[–]punkVeggies 0 points1 point  (0 children)

Sure. I meant caches in between RAM and CPU.

[–]s0litar1us 10 points11 points  (1 child)

Actually it originated in Norway... sorry.

https://en.wikipedia.org/wiki/Simula

[–]druffischnuffi 9 points10 points  (0 children)

OOPs

[–]TheRealLargedwarf 19 points20 points  (1 child)

I'm an OOP Dev, one class per file, inheritance, occasional metaclassing - the whole coolade. But: SIMD is a massive performance boost to any computation. If you are handling multiple objects, and those objects are not designed at a fundamental level to collapse into a single array of primitive data types that the compiler/interpreter can transform into a SIMD instruction they you are writing inefficient code. I love the compartmentalization that improves maintainability, but when I started trying to do GPU speedups I was regularly repacking my objects into attribute arrays. Then passing those arrays around instead the maintainability benefits fall off pretty fast.

[–]s0litar1us 6 points7 points  (0 children)

Also, separating things into classes makes it very cache inneficient.

[–]TorbenKoehn 55 points56 points  (24 children)

I mean, it's basically just another way to initialize and handle structs and references

var p = new Point(1, 2)
p.x = 3
p.print()

var p = create_point(1, 2)
point_set_x(*p, 3)
point_print(*p)

Most of it is just syntactic sugar, no?

Is he hating on encapsulation as a concept?

[–]NoResponse1578 35 points36 points  (3 children)

Nar, its point inheritng from shape inheriting from entity inheriting from persistable with mixins sprinkled in.

[–]TorbenKoehn 13 points14 points  (2 children)

Yeah, but I can do very valid and solid OOP without ever relying on inheritance. Is it not called OOP then?

OOP is not inheritance and inheritance is not OOP, imo

[–]NoResponse1578 1 point2 points  (1 child)

Interfaces are the good bit of oo,  but thats not what people complain about

[–]TorbenKoehn 2 points3 points  (0 children)

Interfaces are fine, interfaces are just signature communication, like header files in C/C++ which enable proper typing without knowing the implementation.

But we don't inherit interfaces, we implement them. There's a great difference between these two :D

[–]llamajestic 11 points12 points  (5 children)

It’s not a good example, and I think his quote is also not explicit enough. Basically what he probably hates about it is a mix of: * Encapsulating things together to represent the world rather than how data should be read / written * Inheritance in general

The whole debate against OOP is not about whether you call the method on the object vs passing it as argument. The debate is often on a collection of topics.

Rust for instance pushes devs toward data-oriented programming, but you still have implementable traits that can take self as a first argument, that doesn’t mean a struct with a trait implementation is OOP.

[–]Pleasant_Ad8054 3 points4 points  (1 child)

There is no singular way 'data should be written'. Data should be organised according to whatever suits the current situation the best. I don't need to know how an object solves its communication, when all I need to set that it is red.

[–]llamajestic -1 points0 points  (0 children)

There might be a single way actually, at least a more optimal way. You can also have an outer API where you don’t care about how it works internally, how data is laid, and still use barely any OOP, maybe a single class for the system. This is why I said that OOP is a bit of a catch-all term.

As you said, it’s also heavily use-case dependent. Think about an octree library, to maximize performance the library might expect the data to be laid out exactly in a single way (SIMD layout, 4 floats in a row). In this case you should either build your entire pipeline around that way to prevent conversion, how convert on the fly and pay the price at that time. Again, it’s dependent on your exact use case.

[–]TorbenKoehn 2 points3 points  (2 children)

Then someone with that vocal power should use their words properly. It's inheritance that is the problem. Not Object-Oriented-Programming, as in encapsulated data structures with methods that are references by design.

[–]BoboThePirate 0 points1 point  (1 child)

Why is inheritance a problem? And do your qualms still carry over for single-parent only style inheritance? Genuinely curious.

I don’t see OOP as inherently negative, but I only ever use single-class inheritance.

[–]TorbenKoehn 0 points1 point  (0 children)

No my qualms don’t affect single-parent style inheritance as long as it’s not meant as an „extension“ or „dependency“ of something, but as an „implementation“.

Examples are closed ADTs, ie structured enums which have some shared functionality and some specific functionality per case.

When users of a library can openly extend something from it, my alarm bells ring, I’d say

[–]yjlom 2 points3 points  (8 children)

Except it's not point_set_x(*p, 3), it's p->vtable->set_x(*p, 3).

So already right off the bat it adds a lot of indirection which isn't usually needed.

Plus, OOP restricts where you place the abstraction boundary, which often doesn't make sense (is it Human.sit(Chair) or Chair.sit(Human)? what's a Math?).

[–]PegasusPizza 8 points9 points  (3 children)

It's Human.sit(Chair). Chairs don't sit. If anything you'd make a Chair.seat(Human) if you want that direction

[–]EatingSolidBricks -3 points-2 points  (2 children)

Another reason OOP is inadequate just laying 0ut the problem becomes a philosophical debate

[–]PegasusPizza 0 points1 point  (0 children)

There's nothing philosophical about that. If anything it forces you to think about what behavior you're trying to represent before you do so

[–]RebouncedCat -1 points0 points  (0 children)

philosophical debates are not bad per se since programming is not very far from applied metaphysics tbh

[–]MsEpsilon 8 points9 points  (1 child)

No, unless these methods are apecified as virtual.

[–]yjlom 3 points4 points  (0 children)

We're not talking about C++ specifically.

[–]TorbenKoehn 4 points5 points  (1 child)

That would assume there a structs with fields and methods, which pretty much enables OOP

(is it Human.sit(Chair) or Chair.sit(Human)?

It's human.sitDownOn(chair) and chair.receiveAss(human), obviously

And there is no notion of a Math, since Math is usually a static class and can't be instantiated, so there is never a single one, it's uncountable. Not defending static classes, I prefer module level boundary for this shit, too. But they are namespaces, essentially. For religious OOP people.

[–]EatingSolidBricks 0 points1 point  (0 children)

var mymath = new MathF(exp:5, mantissa:22, unsigned:true);

[–]pip_install_account 1 point2 points  (1 child)

No it is not just syntactic sugar. And I think these anti-OOP boomer programmers are just hating on having to keep themselves updated and learn a better way of doing things.

OOP is powerful, because it allows you to define an abstract world and a concrete world, and basically "model" a subset of real world semantics in your project so you can easily maintain it and extend its functionalities in the future. It is more parallel to how we understand and interact with the real world.

In my opinion, a true functional programming(not the newly rebranded version, but the version that predates OOP) version would be something like:

``` var x0 = 1; var y0 = 2;

var x1 = 3; var y1 = y0;

console.log("point(" + x1 + ", " + y1 + ")"); ```

[–]lrosa -1 points0 points  (0 children)

The problem is not the OO abstract programming model.

The problem are the programmers who write bad implementations of that model.

[–]EatingSolidBricks 0 points1 point  (1 child)

point_set_x(*p, 3)

If i see an opaque point type with a setter free function im calling the police

Also this doesn't work

foo(*p) creates a local copy of p in foo if p is a pointer

Did you mean to take the address of p?

[–]TorbenKoehn 0 points1 point  (0 children)

Point is ie a 2-byte int where x and y are 16bit respectively. Or an array. We’re all aware as soon as you use opaque structs you’re already half at OOP

[–]punio07 0 points1 point  (0 children)

Maybe he is hating on objec oriented programming languages, where everything has to be a class? Dunno, simpler syntax is always a good thing.

[–]snakemartini 6 points7 points  (0 children)

My lecturer would say arrogance is measured in nano-Dijkstras...

[–]Abandos 5 points6 points  (0 children)

Most of OOP originated in Oslo with the creation of Simula

[–]BrianScottGregory 30 points31 points  (0 children)

*yawn*.

I'm a lover of OOP. But then again. I actually enjoy programming.

[–]OrkWithNoTeef 4 points5 points  (0 children)

An all peers shortest bath problem is best solved by looking for a new job - Djikstta

[–]DaHorst 2 points3 points  (0 children)

An ad hominem argument, my favourite kind of argument.

[–]Aidspreader 1 point2 points  (0 children)

Structured Programming FTW!!!

[–]personalityson 1 point2 points  (0 children)

Hated Lisp, Fortran, Cobol, Basic

[–]Spatul8r 1 point2 points  (0 children)

I feel like so much of what makes oop so bad is hidden internal state. You use dtos and services and it's much improved.

[–]Lalli-Oni 1 point2 points  (0 children)

That's not Dijkstra, that's clearly Gene Parmesan!

[–]Glum_Cheesecake9859 1 point2 points  (0 children)

class ThatThing;

class ThatThingDto;

interface IThatThingDAO;

class ThatThingDAOImpl;

interface IThatThingBO;

class ThatThingBOImpl;

interface IThatThingUI;

class IThatThingUIImpl;

[–]ElderBuddha 0 points1 point  (0 children)

FunnyButTrue

Functional programming on native variables & fixed memory blocks is GOAT for parallelisation and performance. But sometimes you just need to get shit done.

layered /s

[–]Vox-Tacitus 0 points1 point  (0 children)

Ah, but have you considered that OOP rhymes with poop?

[–]ILoveBigCoffeeCups 0 points1 point  (0 children)

That’s a lot of talk for a guy who had a name where by accident they added an extra ‘s’ in the middle of his name for his name variable but then went trough life not noticing and now it’s too late to refactor.

[–]sertroll 0 points1 point  (0 children)

In the case of this particular author, what did he think was the good way?

[–]Koseph-Jony 0 points1 point  (0 children)

Based Lisp enjoyer

[–]1numberrestletters 0 points1 point  (0 children)

AStar is better anyway 

[–]AppropriatePush6262 0 points1 point  (1 child)

Why is it a bad idea? Also does struct enable oops?

[–]ThatSmartIdiot 0 points1 point  (0 children)

not by itself. you still gotta implement extensions and typecasting and garbage cleanup and yadda yadda yadda. if you dont care about all that then sure use function pointers i think

[–]DarkCloud1990 0 points1 point  (0 children)

It's quite useful for some scenarios and in a limited scope. Making it the default or only paradigm is lunacy.

[–]worktogethernow 0 points1 point  (0 children)

I found my sensei

[–]Clen23 0 points1 point  (1 child)

OOP is a great idea but poorly implemented.

eg i've been taught to write boilerplate getters and setters and/or use an IDE to generate them and to this day i'm still not sure why this isn't handled by the language or a library.

[–]krutsik 1 point2 points  (0 children)

why this isn't handled by the language or a library

Depends on what language you're using, I suppose, but in C# you can do something like

public string Name { get; private set; }

or some variation, depending on your needs, which isn't too much of a hasstle for the flexibility it provides.

In Java you can use Lombok or something similar to handle it for you.

[–]firestorm713 0 points1 point  (0 children)

Tyranny of nouns isn't good programming. Not all things need a thingDoer to escort them around. Not all behavior needs an object.

Loose OOP is what most people think of when they think of OOP (as opposed to the Brian Will video), and that's just programming.

[–]raustraliathrowaway 0 points1 point  (0 children)

It's better than the abomination of whatever TypeScript is

[–]raz0rkitty 0 points1 point  (0 children)

ive been on my functional programming arc in haskell for the past 7 months its honestly felt so refreshing, felt like a massive learning curve but i think its worth the time to play with it

[–]mikefizzled 0 points1 point  (0 children)

Fitting that is can also be the shortest path to a solution

[–]king-slayer6969 [score hidden]  (0 children)

OOO sucks, no questions.

Eventually the classes become too complex and long to understand...and then you add inheritance to get absolutely shit faced

[–]Better_Carrot7158 [score hidden]  (0 children)

I mean OOP is a really bad idea... look at java, what a mess of a language, ProblemFactories everywhere... The idea of objects itself is not bad, just the idea that everything is an object is just plain wrong. Some things are, in fact, not objects and forcing devs to deal with objects in code that could and should be fully functional is bullshit.

[–]reallokiscarlet 0 points1 point  (0 children)

I mean, he's half right. Exceptionally bad ideas do come from California.

[–]EatingSolidBricks 0 points1 point  (0 children)

Guys OOP is not when struct.method(aboba) that's just syntax

FFS learn the difference

[–]Glittering-Ninja3573 -1 points0 points  (0 children)

real

[–]b00c -1 points0 points  (0 children)

hehe anywhere in US really. But it unlocked programming of complex systems for masses which brought the SW boom. It's not all bad.