logoalt Hacker News

rramadass01/23/20251 replyview on HN

I pointed you to a specific book i.e. OOSC2 and three specific chapters in it (to start with) which explain the concepts well with examples you asked for. How much more specific can one get? If you already know contracts then it should be easy to translate the concepts to any language of your choice. Meyer provides a thorough rationale and is extremely detailed in his examples. Furthermore, i also pointed you to one of the largest and commercially most successful class library and application framework (i.e. MFC) where you can see classic OOD/OOP (including upcalls/downcalls) in action; and yet you say i am "speaking in generalities"! It seems you are not willing to read/study but expect a couple of paragraphs to enlighten everything, which is not going to happen.

Eg: Base.method() has {pre1} and {post1} as contracts. Derived.method() has {pre2} and {post2} as contracts. What should be the relationship between {pre2}&{pre1} and {post2}&{post1} to enforce proper subtyping?

> This is a common frustration I have with OOP discourse, it tends to be really up-in-the-air and not grounded in concrete specifics

It is not up-in-the-air when ideas and specific books by authors like Bertrand Meyer and Barbara Liskov (both researchers and practitioners) are being pointed out. Trying to simplify their concepts into a couple of paragraphs would invariably miss important nuances and lead to misinterpretations (the bane of most HN discussions based on trivial articles/blog posts). Hence it is better they are studied directly and then we can have a discussion if you would like.

> Meanwhile, users suffer in ways that just don't happen with typeclass-based polymorphism, and none of this discourse is required in my world. So why should I not recommend everyone use typeclass-based polymorphism?

Sure, there are other types of polymorphisms which can be better in certain scenarios. But that is not under discussion here; we are talking about "traditional" dynamic runtime dispatch based polymorphism which is far easier to understand and implement even in small languages like C.

> No, being too flexible is a weakness, not a strength. At scale, rigorous discipline enforced by tooling is required.

Flexibility increases your "design space" and hence never a weakness. Rigorous discipline is needed throughout development but tooling can only do so much.

> In particular, invariants like "no downcalls" or "no upcalls" should 100% be enforced by automation.

This depends on the concept you are trying to express and cannot be the same in all scenarios (except for direct ones like "interface implementation").

> I'd rather not?

Well, you did ask for a concrete example and i showed you MFC apps.

> This sounds really bad to me at scale and under pressure.

Saying something is "bad" or "spaghetti" without understanding the design concepts behind the implementation is wrong. MFC is one the largest and most successful application frameworks in the industry and has proven itself in all sorts of applications at scale; studying it teaches one lots of OOD/OOP techniques (good/bad/ugly) needed in real-life industry apps.


Replies

sunshowers01/24/2025

> Flexibility increases your "design space" and hence never a weakness.

This is just objectively false. Constraints liberate and liberties constrain.

> Rigorous discipline is needed throughout development but tooling can only do so much.

Have you used Rust? I would recommend building some kind of non-trivial command line tool with it — you will quickly see how low your expectations for tooling have been.

> Eg: Base.method() has {pre1} and {post1} as contracts. Derived.method() has {pre2} and {post2} as contracts. What should be the relationship between {pre2}&{pre1} and {post2}&{post1} to enforce proper subtyping?

As someone who understands variance etc quite well, my answer is to simply not have subtypes. You absolutely do not need inheritance subtyping to build production software. (Rust has subtyping and variance only for lifetime parameters, and that's confusing enough.)

> Sure, there are other types of polymorphisms which can be better in certain scenarios. But that is not under discussion here; we are talking about "traditional" dynamic runtime dispatch based polymorphism which is far easier to understand and implement even in small languages like C.

I use traits for runtime dispatch in Rust all the time?

Inheritance is only traditional because C++ and Java made it so. I think it's been a colossal mistake.

show 1 reply