Live data from Hacker News

Inheritance was invented as a performance hack

catern.com

221–230 of 268 posts

Re: Inheritance was invented as a performance hack

#221

After reading all the comments of many confused and curious, here is when inheritance is bad and why so _in the absence of any performance considerations_. First, inheritance from an interface/trait is totally okay. The problem is class inheritance, meaning implementation inheritance. There are two cases: 1) you inherit from a class and only add methods but don't overwrite anything. This is the good case, you can do…

In your view, would this critique also apply to a trait-based version in Rust which supplies a default implementation for `push_all`?

https://play.rust-lang.org/?version=stable&mode=debug&editio...

    trait Stack {
        fn push(&mut self, element: i32);
        fn push_all(&mut self, all: Vec) {
            for element in all {
                self.push(element);
            }
        }
        fn pop(&mut self) -> Option;
    }
As far as I know it's impossible to invoke `super` to get at the default version of `push_all` from an `impl` which overrides `push_all`.

This is still "implementation inheritance", because the implementing type inherits the default implementation of "push_all". But it seems less brittle than classical OOP implementation inheritance.

• No "super" invocations.

• Shallow inheritance hierarchies.

• No direct member variable access from the trait (interface) code.

Re: Inheritance was invented as a performance hack

#222
post #123

Earlier quoted context omitted.

It's the main reason why inheritance became so popular and so useful. Specialization remains a very common design pattern that is incredibly useful and trivially and intuitively solved with inheritance. No other programming concept (HKT, ad hoc polymorphism, functional programming, etc...) comes close to its elegance.

Code-reuse via "Implementation Inheritance" is completely unnecessary for specialisation or polymorphism. When someone says that "inheritance is bad for code reuse" they're not talking about interfaces, or using inheritance for polymorphism. They're strictly talking about sharing code using implementation inheritance, which is the thing that has been widely criticised for more than 30 years now. One can argue that ev…

Reusing implementation is the point.

Here is more specifically what I meant with my comment about specialization above. There is a class with four methods, three of which are exactly what you need but the fourth one, you need to modify.

Solving this with inheritance is trivial (extend and override).

Solving this with any other paradigm is... much harder and requires a lot more boilerplate.

Re: Inheritance was invented as a performance hack

#223
post #184

Earlier quoted context omitted.

You actually can have a single queue that handles the different cases. You want dynamic dispatch for that, the syntax looks like this (quick addition to the playground sample): https://play.rust-lang.org/?version=stable&mode=debug&editio...

... which does put both cars and bicycles in the same queue, but doesn't eliminate the copy-paste for each new type completely; 'car' and 'bicycle' still wind up with separate 'get_speed' impls, which are textually identical aside from the type names.

Sure, traits talk about functions, not properties/ strict fields, so you need to provide trivial getters if you want to abstract over properties.

True, but not that interesting? Sure we could have some Ruby :get_attrs magic or whatever.

Re: Inheritance was invented as a performance hack

#224
post #199

Earlier quoted context omitted.

I think thanks to Java opting to use "implements" for interfaces, people no longer associate "inheritance" as the thing we do when we write a fully abstract class (i.e. interface) and then "inherit" this abstraction to implement it. Interfaces are, of course, crucial. Not sure I understood your example about the static class vs. function factory tbh though.

But I don't think a "fully abstract class" is the same thing as an "interface", at least in Java? As far as I know, you can implement multiple "interfaces" but you can still only "extend" one abstract class, even if it is "fully abstract" in that it has no concrete member variables and all methods are abstract.

In Java it isn't but this is specific to Java (and clones of Java like C#).

This is because Java has single-inheritance enforcement for classes.

C++ for example has multiple inheritance. So the way you do an interface is you just write an abstract class, then extend it to implement it.

Re: Inheritance was invented as a performance hack

#225
post #216

Earlier quoted context omitted.

Recently experimented a bit with Rust and I found the reverse to be true. You cannot compose types in Rust. You compose behaviors not types. Very important distinction as I found out the hard way. Take the following example I have found on the net: https://play.rust-lang.org/?version=stable&mode=debug&editio... In that example, both the bicycle and the car have the property `speed`. Imagine you have multiple types no…

You can use a macro instead of copy and pasting. You can also do this: struct Vehicle { speed: f32, type: VehicleType } enum VehicleType { Car(...), Bicycle(...) } Or this (although this is the least common): struct Vehicle { data: VehicleData type: Box } struct VehicleData { speed: f32, } trait SpecificVehicle { fn quack(&self, data: &VehicleData); } impl SpecificVehicle for Car {...} impl SpecificVehicle for Bicycl…

[deleted]

Re: Inheritance was invented as a performance hack

#226

In my experience, 90% of the time people use inheritance but they really only cared about composition, and their language simply does not have any convenient facility to compose types and re-export their methods. With a good type system that includes traits, you almost never need virtual dispatching, as any Rust developer may tell.

Recently experimented a bit with Rust and I found the reverse to be true. You cannot compose types in Rust. You compose behaviors not types. Very important distinction as I found out the hard way. Take the following example I have found on the net: https://play.rust-lang.org/?version=stable&mode=debug&editio... In that example, both the bicycle and the car have the property `speed`. Imagine you have multiple types no…

Beyond some small changes I would make to use new-types all over the place, I personally like to rely on Deref impls to mimic inheritance (although not everyone agrees this is a good idea to do too often): https://play.rust-lang.org/?version=stable&mode=debug&editio...

As you can see I also used a macro by example there to remove some of the duplicated code that you would otherwise have.

Re: Inheritance was invented as a performance hack

#227

Earlier quoted context omitted.

Very true. I'm checks cloc 14,000 lines into a personal project and I have yet to feel any need to use `dyn` (virtual dispatching for non-Rust folks).

"I've designed this project in Go following Go naturally imposed design patterns and found that I did not need inheritance.", said the Go programmer. "Well, mmm, duh?", thought programmers of other languages. "I've designed this project in Rust, following Rust-imposed design principles, and found monomorphism sufficient", said the Rust programmer. "Well, mmm, duh?", thought programmers of other languages. "I've desig…

Absolutely, but the interesting bits are in the metrics. How many lines of code does it take in each language? How many distinct concepts are required for the solution? To what degree is the mastery of each concept required?

Re: Inheritance was invented as a performance hack

#228

Earlier quoted context omitted.

While it's true that it's easy to implement inheritance in dynamic languages, it's not quite this easy. The most important feature of inheritance is the function override support / virtual dispatch, so that o1.foo() and o2.foo() can do different things, but o2.foo() can also access o1.foo() (by using something like super.foo() in Java or BaseClass::foo() in C++). Ideally this would also be optimized so that each obje…

Override support, no? function f() { function g() { return 1 } return { g } } function f1(f) { function g() { return f.g() + 1 } return { g } } f1(f())

Sure, but that's a whole bunch more boilerplate than just .assign(), which was my point (it's easy, but not quite as easy as just calling assign). Especially since f needs to be written a certain way to be overridable, while in the previous example o1 was just an ordinary object.

Edit: Not to mention that storing function pointers with each object is a huge waste of memory, versus storing one class pointer in each object, and having that class object store function pointers and parent class pointers. That way, you guarantee that each function pointer is stored exactly once, and each object has a single sizeof(pointer) of overhead (and even this can be improved with more complex implementations).

Re: Inheritance was invented as a performance hack

#229
post #111

The original inspiration for the idea really has nothing whatever to do with its subsequent architectural role, nor with its value as a mechanism. As a performance optimization, inheritance demonstrated value at a time when performance improvements were at least three orders of magnitude more important than they are today. People here like to disparage OO and inheritance, but the distaste clearly is just a reaction t…

Implementation inheritance is indeed problematic, no matter how it's used. The defining feature of implementation inheritance is that any code, relating to any class in the hierarchy, can rely on methods that may then be overridden in unpredictable ways further down in the hierarchy. If you don't need or expect this behavior, you can use composition and delegation instead - which come with a far simpler semantics and…

Implementation inheritance is absolutely no problem within a project or component that entirely controls both base and derived classes, where it amounts to, simply, a notational convenience. It is not, then, a "good OO design"; but there is nothing sacred about OO. Ultimately, any combination of well-specified mechanisms may be correctly used to achieve an elegant design, regardless of formal architectural conventions. The reason a feature was introduced into a language has no necessary connection to reasons for using it.

Implementation inheritance is a problem when crossing organizational boundaries, as reasonable changes upstream can impact correctness downstream.

Ultimately, there is no substitute for taste. Substituting fetishism produces unfortunate results.

Re: Inheritance was invented as a performance hack

#230
Subtyping with inheritance is kinda complicated from a theoretical perspective, so the trade-off in java-like languages seems to usually be to neuter the type system, by restricting type-inference and rely on nominal types specified by the programmer.

After spending way to much time in Java, and OOP-style php and python, I just struggle to see the advantage of inheritance. It usually takes a long time to look through the object hierarchy to figure out where the behavior is really coming from.. and the loss of type inference outside of localized expression statements is devastating in comparison.

Post reply on HN