Pike's quote from that interview:
(...) let me say that I'm not much of a fan of object-oriented design. I've seen some beautiful stuff done with OO, and I've even done some OO stuff myself, but it's just one way to approach a problem. For some problems, it's an ideal way; for others, it's not such a good fit.
Here's an analogy. If you want to make some physical artifact, you might decide to build it purely in wood because you like the way the grain of the wood adds to the beauty of the object. In fact many of the most beautiful things in the world are made of wood. But wood is not ideal for everything. No amount of beauty of the grain can make wood conduct electricity, or support a skyscraper, or absorb huge amounts of energy without breaking. Sometimes you need metal or plastic or synthetic materials; more often you need a wide range of materials to build something of lasting value. Don't let the fact that you love wood blind you to the problems wood has as a material, or to the possibilities offered by other materials.
The promoters of object-oriented design sometimes sound like master woodworkers waiting for the beauty of the physical block of wood to reveal itself before they begin to work. "Oh, look; if I turn the wood this way, the grain flows along the angle of the seat at just the right angle, see?" Great, nice chair. But will you notice the grain when you're sitting on it? And what about next time? Sometimes the thing that needs to be made is not hiding in any block of wood.
A fairer analogy would be to say that OOP is a particular architectural style, and FP is another.
You can mix Japanese woodworking with European woodworking by using screws, nails and glue whenever you feel like it. Its simpler than pure Japanese woodworking, not as pretty, quicker to construct, doesn't last as well. You can even forego Japanese woodworking entirely, but to do so is to throw away an entire culture's worth of knowledge about how to cut, shape and join wood, so likely not a great idea.
I, personally, am a believer in minimalism. Features which experts tell beginners to avoid should be removed. That's very hard to do with programming languages that need to support legacy code though. So I always look for new programming languages that try to do things differently, with as few features as possible.
Analogy: You talk to some master chefs. You tell them that the sharp knives are dangerous; they should get rid of them. They're going to tell you to get lost. The knives are valuable to them precisely because they are sharp. Sure, an amateur like me could seriously cut himself on one. So?
I don’t think your analogy is appropriate though. My original analogy, the Swiss Army knife, conveys what I think is the core of the problem with OOP: it takes unrelated, powerful tools, and ties them together in a way that makes them unwieldy and more difficult to use.
This problem is inherent to the object itself. It ties together unrelated concepts: polymorphism, data abstraction, mutable state, inheritance, and modules. Hence the Swiss Army knife analogy.
Other languages (namely functional languages) give you all of these powerful tools but you get them separately, so they’re easier to control.
OK, "I wouldn't work on such a project" is a fine answer. But not a universal one.
1) performance bottlenecks are generally no longer CPU computation (network tends to dominate for many many things), and there is nonzero overhead to most functional operations.
2) coordination-free concurrency can in many cases often overcome computational bottlenecks, and non-OOP is far more effective for the code author in dealing with/grokking concurrency.
3) in the intervening time, functional patterns for some things (like UI) have been discovered which make fp a good fit for things that in the past were understandably the domain of OOP.
In order to build reuseable components you need components that compose well. Object composition does not allow for composition without specific glue code for each integration.
For example:
functions addOne and addTwo can compose to form addThree with a single general function composition operator, no glue code needed and no need for addOne or addTwo to be aware of each other. (addThree = addOne . addTwo)
OneAdder and TwoAdder cannot compose without specific glue code or specific interface code to allow for DI. ThreeAdder = composeAdder(OneAdder, TwoAdder) would be implementation specific. Additionally ThreeAdder(TwoAdder, OneAdder) makes ThreeAdder aware of TwoAdder and OneAdder. These are not qualities of reuseability. The existence of a mutating object breaks reuseability.
OOP does have it's use cases but the hate does comes from somewhere. One of the main downsides of OOP is the fact that current practices makes it one of the least reusable paradigms ever. DI and object composition are illusions. That being said it's not all that bad... not all business apps need the form of extreme reuseability that FP provides, so OOP is often good enough.The point of the example wasn't to talk about naming. It was to talk about a fundamental structural problem with OOP.
"There are 2 hard problems in computer science: cache invalidation, and naming things"
or its cousin:
"There are 2 hard problems in computer science: cache invalidation, naming things and off-by-one errors".
But OOP languages do have lambdas and function pointers, so can easily do something very close to that. In Kotlin:
fun addOne(value: Int) = value + 1
fun addTwo(value: Int) = value + 2
val addThree = fun (value: Int) { addOne(addTwo(value)) }
There are libraries that add a pure functional composition operator via a + operator overload, I think.Even in Java you can do function composition, though it's not well known and a bit verbose:
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
static int addOne(int i) { return i + 1; }
static int addTwo(int i) { return i + 2; }
void combine() throws Throwable {
var lookup = lookup();
var type = methodType(int.class, int.class);
var first = lookup.findStatic(Main.class, "addOne", type);
var second = lookup.findStatic(Main.class, "addTwo", type);
var combination = filterReturnValue(first, second);
System.out.println(combination.invoke(1)); // Prints 4
}