I don't know if I see eye-to-eye with grandparent but I'll give it a shot!

As others have pointed out, everyone has a different definition of what the core principles of OOP are.

When Alan Key coined the term "object-oriented programming", he had message passing (among other things) in mind. If you like that definition, stop reading this and go use Erlang. If you were more thinking of the various modern languages we now call OOP, read on.

The "lies for small children" version is that OOP is all about modelling the real world in a natural way: apple.eat(), car.drive(), etc. Nouns are objects and verbs are methods. From my POV it's a strawman to reduce OOP to this, but I bring it up because some people like it this way and will argue for it - "It's a natural way to think about software", etc.

How are nouns and verbs actually modelled in practice? You don't file.read() and file.write(), you construct a FileReader and FileWriter, and you tell them to read() and write(). The verbs ofen show up in class names (fileREADer). It's delegation of responsibility and I argue it's a good way to design software. But delegation of responsibility is not restricted to OOP.

Putting aside modelling and design concerns, what are the core features of OOP languages? I like the Uncle Bob definition of Inheritance, polymorphism and encapsulation.

Inheritance:

Inheritance is fraught with more "lies for small children". A Lion is a type of Cat which is a type of Animal. I admit this is another strawman because using inheritance to model taxonomies like this is dumb and doesn't happen in the real world. How is inheritance used in the real world? I honestly don't see it that often. A lot of frameworks like to hijack your 'main' method and force you to use inheritance to run the thing, e.g. MyApp extends SpringApp. I keep trying to think of inheritance examples and think "no, that's just an example of using an interface".

Anyway, one of the principles in the OOP body of knowledge that has emerged over time is to "favour composition over inheritance". You might get to share some behaviour and cut down some code between 2-3 classes, but on the flipside, now when you want to modify just 1 class, you have to realise you're modifying the other 2 classes as well.

Elsewhere in the OOP body of knowledge is the five SOLID principles. I love SOLID. Someone familiar with SOLID might say "but we know how to safely extend classes because of the (L)iskov Substitution Principle!" But it's not an instructive way to safely use inheritance. It basically says "don't fuck up inheritance".

Polymorphism:

Lots of languages have polymorphism. I mainly use Java and I'm disappointed in its polymorphism story. Perhaps other OOP languages do it better? Java has interfaces/abstract-classes as well as "Generics" (first-order parametric polymorphism). First-order parametric polymorphism will let you model List<Integer> as List<I>, but it will not let you model it as L<Integer>. Why does this matter? When you want to abstract over things that are "mappable", like Optional and Stream.

<U> Optional<U> map(Function<? super T,? extends U> mapper);
<R> Stream<R> map(Function<? super T,? extends R> mapper);

I can't do this:

<U> F<U> map(Function<? super T,? extends U> mapper); // The F stands for "Forbidden" !

I have worked on a large codebase where one of my tasks was to move from Guava Futures to Java 8 CompletableFutures. I really would have liked the ability to hide those concrete classes behind some kind of abstraction. Incidentally later on one of my colleagues said he'd like to rewrite it using Observables. At a different company one of my coworkers went the other way - he did a "hack week" project of converting a codebase from Observables to Futures. It would be better if you could just leave the business logic generic and instantiate it over an Observable or a Future as you saw fit. I don't know OO languages that let you do this.

Encapsulation:

I think the most promininent definition of encapsulation is you hide data inside an object and only manipulate that object using its (public) methods. Why might you want to hide data in such a way? So you can delegate responsibility to the object, because it will handle the data better than you will from outside the object. You don't need objects for that. You can do that with data and functions - unless you insist upon the apple.eat() syntax instead of eat(apple) discussed in paragraphs 2-3.

NB. Putting state inside an object doesn't make it safe for concurrent modifications. But that's a whole 'nother discussion topic. In the "real world" I rarely need to use a mutable data field in an object. Fields are generally just other dependencies, e.g. database handles or other services.

I think I did a reasonable job of hitting "the core principles". It's not so much that they're "bad ideas", but rather, once you strip away the large, good majority of ideas which is available outside OOP (e.g. delegation of responsibility) you're just left with the bad ideas (e.g. inheritance) or ideas which have been executed better elsewhere (polymorphism, message passing.).