Live data from Hacker News

Inheritance was invented as a performance hack (2021)

catern.com

221–230 of 252 posts

Re: Inheritance was invented as a performance hack (2021)

#221

Earlier quoted context omitted.

Inheritance is not the only way to share behavior across different implementations — it'a just the only way available in the traditional 1990s crop of static OOP languages like C++, Java and C#. There are many other ways to share an implementation of a common feature: 1. Another comment already mentioned default method implementations in an interface (or a trait, since the example was in Rust). This technique is even…

To my mind, the challenge is not "sharing behavior"; it is "sharing behavior in a way that capture human-understandable semantics and make code easier to reason about instead of harder." I suspect part of the problem of inheritance is that it is a way to share behavior that some humans, especially visual thinkers who understand VMTs, find easy to reason about. In my experience verbal thinkers struggle with inheritanc…

> In my experience verbal thinkers struggle with inheritance, because it requires jumping between levels of abstraction and they aren't thinking in terms of semantic units.

This is too dismissive of the criticism. The problem with inheritance is it makes control flow harder to understand and it spreads your logic all over a bunch of classes. Ironically, inheritance violates encapsulation - since a base class is usually no longer self contained. Implementation details bleed into derived classes.

The problem isn’t “verbal thinkers”. I can think in OO just fine. I’ve worked in 1M+ line of code Java projects, and submitted code to chrome - which last time I checked is a 30M loc C++ project. My problem with OO is that thinking about where any given bit of code is distracts me from what the code is trying to do. That makes me less productive. And I’ve seen that same problem affect lots of very smart devs, who get distracted building a taxonomy in code instead of solving actual problems.

It’s not a skills problem. Programming is theory building. OO seduces you into thinking the best theory for your software is a bunch of classes which inherit from each other, and which reference each other in some tangled web of dependencies. With enough effort, you can make it work. But it almost always takes more effort than straightforward dataflow style programming to model the same thing.

Re: Inheritance was invented as a performance hack (2021)

#222

Earlier quoted context omitted.

Yeah, the problem with OO isn’t really in the languages. The problem is in the community, and what people consider “best practice”. C#, Java and C++ are all arguably multi-paradigm languages. They give you a lot of flexibility in how you structure your code. C# and C++ support value types. Modern Java has great support for a lot of FP concepts too. So I agree with you. You can write good C# if you want to. The proble…

Maybe we need to tease "community" apart from language. Let's have Java / C# "A" people (who need at least 10 levels of inheritance, gotta use DI, insist on every character of SOLID (and actually remember and care about the Liskov substitution principle - and insist that it wasn't chosen simply because it starts with "L" and makes the acronym sound better) and have never written any code that added any value - only f…

Yep. That’s why I prefer to criticise OOP (and in particular, inheritance). Not specific languages.

I met this old guy at a conference one, ~15 years ago. He said he didn’t get why people say Java is slow. His Java, he said, runs just as fast as C. I asked him to show me his code - and I’m so glad I did. It was amazing. He did everything in one big static class, and treated Java as if it were a funny way to write C. He ignored almost the entire standard library. No wonder his code ran fast. It was basically JIT-compiled C code.

Java isn’t the problem. “Java best practices” are the problem. It’s a culture thing. Likewise, can write heavily OOP code in C if you really put your mind to it and write your own struct full of function pointers. But it’s not in culture of the C community to overuse that design.

Re: Inheritance was invented as a performance hack (2021)

#223

Earlier quoted context omitted.

>Why would we assume that? If the objects are entirely distinct, why are you combining them together into one class at all? That doesn't make any sense. Let distinct types be distinct. Let consumers of those types combine them however they like. Human = torso, legs, arms. Three distinct objects combine into one thing. A human by definition is the union of these things. It's fundamental. It's just your bias is trying…

> Human = torso, legs, arms. Three distinct objects combine into one thing. A human by definition is the union of these things. It's fundamental. It's just your bias is trying to see it as something else. No, it’s not. If I put a torso, legs and arms (and perhaps a head) on a table, I don’t get a human being. I’d say a human composes all of those things (and more!). But a human doesn’t inherit from them. For example,…

I’m not gonna read the full thing. I’m just responding to the first part. Composes or inheritance is just vocabulary for two things that are the same with slight differences.

Composes duplicates identical properties via nesting.

Inheritance overrides properties that are identical.

That’s it. I’m done.

Re: Inheritance was invented as a performance hack (2021)

#224
post #51

Earlier quoted context omitted.

> I don't think Inheritance is always bad - sometimes it's a useful tool. I can only think of one or two instances where I've really been convinced that inheritance is the right tool. The only one that springs to mind is a View hierarchy in UI libraries. But even then, I notice React (& friends) have all moved away from this approach. Modern web development usually makes components be functions. (And yes, javascript…

> But even then, I notice React (& friends) have all moved away from this approach. Modern web development usually makes components be functions. But what do those functions return? Oh look, it's DOM nodes, which are described by and implemented with inheritance. I would agree that view hierarchies in UI libraries are one of the primary use-cases for inheritance. But it's a pretty big one.

> But what do those functions return? Oh look, it's DOM nodes, which are described by and implemented with inheritance.

Well of course. React builds on what the browser provides. And the DOM has been defined as a class hierarchy since forever. But react components don’t inherit from one another. If the react devs could reinvent the DOM, I think it would look very different than it looks today.

Re: Inheritance was invented as a performance hack (2021)

#225
post #157

Earlier quoted context omitted.

It is a good idea because it's the most fundamental idea. You have two objects. A and B. How do you merge the two objects? A + B? The most straight forward way is inheritance. The idea is fundamental. The reason why it's not practical has more to do with human nature and the limitations of our capabilities in handling complexity then it has to do with the concept of inheritance itself. Literally think about it. How e…

> How else do you merge two structs if not using inheritance? By merging them. Structs are product types. If you merge them, you get a bigger product type. You don't need inheritance (ADTs) for that. The more useful point of inheritance is having shared commonality. But modern languages make it convenient to express that without using ADTs/inheritance. TypeScript is fully structurally typed. If you combine a Foo and…

>By merging them. Structs are product types. If you merge them, you get a bigger product type. You don't need inheritance (ADTs) for that.

Merging structs and inheritance are fundamentally the same thing.

>How fundamental is inheritance if it can be fully replaced by simpler building blocks?

It can't be replaced. Combining Foo and Bar in the way you're thinking involves additional primitives and concepts like nesting. If Foo and Bar share a same property the most straight forward way of handling is overriding one property with the other. Overriding IS inheritance.

We aren't dealing with product types in the purest form either. These product types have named properties and you need additional rules to handle conflicting names.

In fact once you have named properties the resulting algebra from multiplying structs is not consistent with the concept of multiplication whether you use inheritance or "object composition"

Re: Inheritance was invented as a performance hack (2021)

#226

Earlier quoted context omitted.

When you call myImageInstance.fetchMetadata, what does it do? I don't know rust, so it's not clear to me how the value gets cached.

In this example, ThingWithMetadata does the caching. image.fetch_metadata fetches the image and returns it. It’s up to the caller (in ThingWithMetadata) to cache the returned value.

But part of the goal is to not need the caller to cache it. Nor have the class that knows how to fetch it need to know how to cache it either. The responsibility of knowing how to cache the value is (desired to be) in the MetadataSource interface.

Re: Inheritance was invented as a performance hack (2021)

#227

Earlier quoted context omitted.

> Human = torso, legs, arms. Three distinct objects combine into one thing. A human by definition is the union of these things. It's fundamental. It's just your bias is trying to see it as something else. No, it’s not. If I put a torso, legs and arms (and perhaps a head) on a table, I don’t get a human being. I’d say a human composes all of those things (and more!). But a human doesn’t inherit from them. For example,…

I’m not gonna read the full thing. I’m just responding to the first part. Composes or inheritance is just vocabulary for two things that are the same with slight differences. Composes duplicates identical properties via nesting. Inheritance overrides properties that are identical. That’s it. I’m done.

Maybe that's our ultimate disagreement. I think the difference between composition and inheritance matters a lot. It changes how we break our software into modules. It sounds like you think of inheritance as being fundamentally the same as composition "with slight differences". Although even you admit that "Our brains cannot handle the complexity [of inheritance]". If that's true (and I think it is), the difference is surely more than skin deep.

I agree about nesting. But nesting matters, because it forces us to design components which make sense in isolation. As a result, composition encourages - and in many ways requires - better modularity in code. Inheritance does not. Base classes are often poorly conceived, poorly specified grab-bags of state and functions. They lead to hard to understand, hard to follow code.

Earlier in this thread you insulted my intelligence. You said this:

> Don't ever tell me that programming for 30 years is a reason for being correct. It's not. In fact you can be doing it for 30 years and be completely and utterly wrong. Then the 30 years of experience is more of a marker of your intelligence.

I'm curious if you'll still back the argument you've made here after you've been programming for 30 years too. You're clearly already suspicious of how and why inheritance makes code harder to understand. I suspect in a few years, you'll come around to my point of view on this. But I'd love to know if I'm wrong.

Re: Inheritance was invented as a performance hack (2021)

#228

Earlier quoted context omitted.

In this example, ThingWithMetadata does the caching. image.fetch_metadata fetches the image and returns it. It’s up to the caller (in ThingWithMetadata) to cache the returned value.

But part of the goal is to not need the caller to cache it. Nor have the class that knows how to fetch it need to know how to cache it either. The responsibility of knowing how to cache the value is (desired to be) in the MetadataSource interface.

The rule is that you can't cache a value in an interface, because interfaces don't store data. You need to cache a value in a struct somewhere. This implementation wraps items (like images) in another struct which stores the image, and also caches the metadata. Thats the point of ThingWithMetadata. Maybe it should instead be called WithCachedMetadata. Eg, WithCachedMetadata.

You can pass WithCachedMetadata around, and consumers don't need to understand any of the implementation details. They just ask for the metadata and it'll fetch it lazily. But it is definitely more awkward than inheritance, because the image struct is wrapped.

As I said, there's other ways to approach it - but I suspect in this case, using inheritance as a stand-in for a class extension / mixin is probably going to always be your most favorite option. A better approach might be for each item to simply know the URL to their metadata. And then get your net code to handle caching on behalf of the whole program.

It sounds like you really want to use mixins for this - and you're proposing inheritance as a way to do it. The part of me which knows ruby, obj-c and swift agrees with you. I like this weird hacky use of inheritance to actually do class mixins / extensions.

The javascript / typescript programmer in me would do it using closures instead:

    function lazyResource(url) {
      let cached = null
      return async () => {
        if (cached == null) cached = await fetch(url)
        return cached
      }
    }

    // ...
    const image = {
      metadata: lazyResource(url)
    }
Of all the answers, I think this is actually my favorite solution. Its probably the most clear, simple and expressive way to solve the problem.

Re: Inheritance was invented as a performance hack (2021)

#229

Earlier quoted context omitted.

I’m not gonna read the full thing. I’m just responding to the first part. Composes or inheritance is just vocabulary for two things that are the same with slight differences. Composes duplicates identical properties via nesting. Inheritance overrides properties that are identical. That’s it. I’m done.

Maybe that's our ultimate disagreement. I think the difference between composition and inheritance matters a lot. It changes how we break our software into modules. It sounds like you think of inheritance as being fundamentally the same as composition "with slight differences". Although even you admit that "Our brains cannot handle the complexity [of inheritance]". If that's true (and I think it is), the difference i…

>I'm curious if you'll still back the argument you've made here after you've been programming for 30 years too. You're clearly already suspicious of how and why inheritance makes code harder to understand. I suspect in a few years, you'll come around to my point of view on this. But I'd love to know if I'm wrong.

I did insult your intelligence. Because when you said you have 30 years of experience I hear total arrogance. It's like "I'm right and you're wrong because I have 30 years of experience" When I hear that I just want the other person to shut the hell up.

>I agree about nesting. But nesting matters, because it forces us to design components which make sense in isolation. As a result, composition encourages - and in many ways requires - better modularity in code. Inheritance does not. Base classes are often poorly conceived, poorly specified grab-bags of state and functions. They lead to hard to understand, hard to follow code.

Again, you resuse the same code if you don't use inheritance. A cat walks, so does an animal, do does a dog. You have to write walk() twice if you don't use inheritance. There's a trade off here.

The difference is skin deep. It's the emergent complexity that is NOT skin deep.

combining objects via "object composition" or "inheritance" is different. One way is not more right then the other. It's simply that you can't handle the hierarchical relationships.

But think about it. If you have a deeply nested Object where you don't use inheritance. Then all the objects have multitudes of redundant properties, doesn't that result in complex code as well? And how does nesting objects make it less complex then inheriting objects? It's more of code navigation problem in the sense that when you use inheritance and you look at a child derived from generations of inheritance it's just hard to read and figure out what the final object is.

With object composition the view is the same. You have an object that holds generations of nested objects. The difference is you can control click and follow the definition of the nested object so it's more visible.

Thus it seems to me the issue with the complexity is that inheritance simply does not give you a widget you can control click into easily to follow the definition. This whole problem could be characterized by a user interface issue because it's not evident to me how an object with nested objects 1000000 layers deep is more complex then the same object derived from 100000 ancestors.

Re: Inheritance was invented as a performance hack (2021)

#230

Earlier quoted context omitted.

But part of the goal is to not need the caller to cache it. Nor have the class that knows how to fetch it need to know how to cache it either. The responsibility of knowing how to cache the value is (desired to be) in the MetadataSource interface.

The rule is that you can't cache a value in an interface, because interfaces don't store data. You need to cache a value in a struct somewhere . This implementation wraps items (like images) in another struct which stores the image, and also caches the metadata. Thats the point of ThingWithMetadata. Maybe it should instead be called WithCachedMetadata. Eg, WithCachedMetadata . You can pass WithCachedMetadata around,…

> The rule is that you can't cache a value in an interface, because interfaces don't store data.

Right, but the start of where I jumped into this thread was about the fact that there are places where fields would make things better (specifically in relation to traits, but interfaces, too). And then proceeding to discuss a specific use case for that.

> A better approach might be for each item to simply know the URL to their metadata.

Not everything is a coming from a url and, even when it is, it's not always a GET/REST fetch.

> but I suspect in this case, using inheritance as a stand-in for a class extension / mixin is probably going to always be your most favorite option

Honestly, I'd like to see Java implement something like a mixin that allows adding functionality to a class, so the class can say "I am a type of HasAuthor" and everything else just happens automatically.

Post reply on HN