4 ms·
Polymorphism and other important aspects of inheritance
- kleiba 3y agoClojure has spent a considerable amount of design work on polymorphism: https://clojure.org/about/runtime_polymorphism https://clojure.org/about/runtime_polymorphism
- eterps 3y agoThe Oberon programming language supports inheritance and/or polymorphism on different levels of abstraction: http://miasap.se/obnc/data-abstraction.html http://miasap.se/obnc/data-abstraction.html
- CSMastermind 3y agoPerhaps I'm dense but I'm not sure I understand the author's point. In C# I could easily write: public class Person { public string Name { get; set; } public int Age { get; set; } } public class Employee : Person // Employee inherits from Person { public string Company { get; set; } } Whereas in Go I might have to do this for "data embedding": type Person struct { Name string Age int } type Employee struct { Person // This is embedding Person struct in Employee Company string } There's nothing stopping me from doing that in C#. public class Person { public string Name { get; set; } public int Age { get; set; } } public class Employee { public Person person { get; set; } public string Company { get; set; } } > I do think it's important for a programming language to separate these aspects from classical inheritance itself ...they are already separated?
- naasking 3y agoI think reread the first paragraph about Go embeddings and method promotion to get a better idea of what he means by embedding. It sounds more record extension, so your C# example doesn't have the property he's talking about.
- gabereiser 3y agoThis is correct. The last example of adding a Person property to Employee isn’t the same as Go’s struct embedding where Employee gets the fields and structure of Person.
- thangalin 3y agoNone of those Person/Employee examples are pure OOP, FWIW. * https://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented https://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented * https://wiki.c2.com/?AlanKayOnMessaging https://wiki.c2.com/?AlanKayOnMessaging "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme LateBinding of all things." ~ Alan Kay "The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be." ~ Alan Kay The antithesis of OOP is exposing the name and age of a Person to the greater system at large.
- OkayPhysicist 3y agoBringing up Alan Kay's definition of OOP is approximately worthless when it comes to discussions of mainstream OOP. Yes, Erlang is "more OOP" than Java by Kay's definition. But that ship has sailed. People will (justifiably) look at you funny if you claim your functional language is OO, and when asked to name an Object Oriented language will say C#, Java, or maybe C++. That definition is dead. Wish it well, then bid it farewell. "OOP" means "Java" and we can just call Erlang and co "Agent Model".
- pdpi 3y agoThe point is that the Golang version is closer to this: interface IPerson { public string Name { get; set; } public int Age { get; set; } } public class Person : IPerson { public string Name { get; set; } public int Age { get; set; } } public class Employee : IPerson { private Person _person; public string Name { get { return _person.Name; } set { _person.Name = value; } } public int Age { get { return _person.Age; } set { _person.Age = value; } } public string Company { get; set; } } Employee conforms to the same interfaces as Person, but achieves this via delegation (the famous "favour composition over inheritance" thing)
- myk9001 3y agoWhat advantages does the Golang version have over inheritance? Please, note I don't dispute the "prefer composition over inheritance" thing. In fact, I heartily agree with it. But in this specific case, where every sinle detail of Person is publicly exposed in Employee (no pun intended), I don't see a material difference. If anything, the example uses composition as a tool to implement inheritance on top of. Am I missing something?
- hazelnut-tree 3y agoPolymorphism and inheritance: simple or complex ideas? Or is meaning depending on the lanuage? I really like the following video: OOP principles (and why so much jargon). The four principles of object oriented programming (what they are and why they are wrong!) https://www.youtube.com/watch?v=YpYLXq4htKY https://www.youtube.com/watch?v=YpYLXq4htKY > Abstraction, Polymorphism, Inheritance and Encapsulation… Yikes! What the heck does all that mean and why should anyone care? > Programming is full of jargon. Object Oriented Programming takes jargon to a whole new level. But even so, that list of the “four fundamentals of object oriented programming” is missing something important. In this video, I explain what the four principles are. And I then explain why they are making a big deal out of something that is essentially quite simple." Later in the video: > I'd say polymorphism is probably a hundred dollar word for a 50 cent idea. These days computer science teachers and writers have extended the meaning of polymorphism to such an extent that it's often hard to figure out what the heck they're trying to describe.
- zwieback 3y agoFor this type of discussion I think it's important to contrast statically (or manifest) and dynamically typed languages. Lumping together inheritance and polymorphism kind of just happens with the former. With dynamically typed languages you have more freedom to just throw the right functions into your object and informally declare that your object will work with calling function, regardless of the specific type. Of course now you have to talk about semantic polymorphism, e.g. will the outcome of calling some polymorphic functions have the right effect on the system? This is where static type systems have it easier because you typically tie stuff together in related hierarchies. The Go structure embedding thing was a surprise to me, having grown up with C and C++ it looks unexpected. Not sure I like it but I think I do.
- myk9001 3y agoYou're describing nominal and structural typing. This concept is orthogonal to static and dynamic type checking. Go and TypeScript are examples of languages with statically checked structural type systems. While C# has statically checked nominal type system. I don't use dynamic languages much, so maybe someone else could chime in with a few examples.
- pdpi 3y agoThe first step in separating polymorphism and inheritance is understanding that there are several forms of polymorphism. The one that gets lumped in with inheritance is subtype polymorphism, but there's at least two other sorts (not an expert, there may be more?): parametric polymorphism ("generics") and ad-hoc polymorphism (e.g. rust traits, haskell typeclasses). All of these achieve one form of code sharing, which is that, in one way or another, code that only depends on your external interface can be reused with any implementation of that external interface. The other place you want code sharing is the implementation of that external interface. Inheritance is one well-known way to do this, but composition/delegation is another strategy to achieve the same thing. Golang's embedded structs are a pretty streamlined (but fairly limited) way of achieving this, and e.g. Kotlin has explicit delegation: `class Derived(b: Base) : Base by b`. Languages like e.g. Java (where classes are open by default) push you towards inheritance, while languages like Kotlin (where classes are final by default, and where you have explicit support for delegation) push you towards composition. A whole lot of confusion comes from most mainstream OOP languages conflating inheritance and subtype polymorphism.
- ulrikrasmussen 3y agoRow polymorphism is another sort which is only concerned with record types and allows functions to operate on a section of a record without losing type information.
- jrochkind1 3y agoI wouldn't even call "polymorphism" an "aspect of inheritance" -- you can have polymorphism _without_ inheritance (as a code re-use form), and polymorphism is actually more fundamental and importance than inheritance. I feel like this "hot take" is actually a pretty common take. You can have polymorphism without inheritance; you can't really have inheritance without polymorphism. To the extent that inheritance necessarily "includes" polymorphism, so polymorphism is in that sense an aspect -- yeah, it's more important and fundamental. And I think this is actually a pretty common take. All the takes saying inheritance itself is not fundamental to OO (and even sometimes saying OO is better without inheritance), are variations of saying this.
- ozim 3y agoWith nowadays approach where usually people favor composition over inheritance all is true that OO is better without inheritance.
- marcosdumay 3y agoExcept that composition doesn't give you polymorphism. I always though that dichotomy missed the point. You can give-up inheritance for generics, for interfaces (as if those were a different thing), for type classes, or for type ducking; but you can't give-up inheritance for composition because the only thing composition solves is code reuse.
- jrochkind1 3y ago> for interfaces (as if those were a different thing) Interfaces are a different thing than inheritance, precisely because of what we are talking about. Inheritance is about code re-use, and interfaces are about polymorphism. Interfaces are the polymorphism part of "inheritance", while dropping the code re-use part. It is true that composition does not give you polymorphism, absolutely. > because the only thing composition solves is code reuse. Exactly. Inheritance combines polymorphism and code re-use in one form. If you instead don't have inheritance, than interfaces (or something similar by a different name; or "duck-typing" if you don't have static typing and also don't really want any kind of typing at all) give you polymorphism, so what gives you code re-use? Composition. If you split the code re-use function from the inheritance function, interfaces plus composition is the (or one very common/obvious) solution. I don't think we are disagreeing, I mean to show you we are all violently agreeing. :)
- david2ndaccount 3y agoNote that plan9 and Microsoft compilers (and now clang with the right flag) allow the C structure embedding as well, see <https://9p.io/sys/doc/comp.html https://9p.io/sys/doc/comp.html>, search for “Extensions”.
- nsajko 3y agoI think GCC also supports this for a long time already, see -fplan9-extensions and -fms-extensions
- coding123 3y agoTwo things I've learned over 30 years of programming, but mostly the political landscape of software engineering in general are: 1. The code the is used to implement languages or the main "library" of said languages and your code to implement products don't have to and probably should NOT mirror each other. Other ways to think about it: Just because Java standard library has a generic List -> LinkedList or ArrayList etc..., doesn't mean your software related to managing Patients does NOT mean you need this complication: Human <- BillablePerson <- Patient (this is somewhat whacky). I often see developers do things like this because its "a best practice" and maybe there's a 1% of your code that will take advantage of something like that. In reality it makes a lot more sense to just generate a Bill and send it to an Patient. Whether they are a BillablePerson or not doesn't really matter. Anyone opening the user interface can see a payment screen, right? You can flip the status on a Bill to paid, right? Yes the pedants will come running. And yes if you're app is a framework for medical billing software (you're not a billing company but a company that makes billing frameworks) _maybe_ this is useful? But my answer to that is - even in that case, more and more we're outsourcing screens and concepts to other cloud systems. The concept of polymorphism is neat to iterate a LinkedList and an ArrayList, but it's not buying much for an invoice that gets paid in freshbooks and a patient managed in some EHR software. So as much as it's nice to "wrap all the things" the software we write is increasingly outsourced to yet another API call. Last two companies I worked at the config.json file was 50 lines, almost 90% of which is API tokens to various cloud SaaS vendors that take care of billing or email or storage or uploads. Wrap all the things? It's just not feasible anymore. 2. Second thing I've learned: Don't react to Everything. Have a bad deployment? Have some requirements that were lost? You can definitely implement a process or two especially for repeating issues. But don't react to everything. If you make a new policy or process or 2-step program for any frown you generate, you'll never get back to coding up the solution. You'll drag your software developers through red tape.
- commandlinefan 3y ago> does NOT mean you need this complication: > Human <- BillablePerson <- Patient (this is somewhat whacky). Whenever I see something like this I push the implementor to point me to some code that accepts a "Human" as a parameter and actually does something with it (other than casting it down to Patient). If they can't, I try to encourage them to drop the unneeded hierarchy.
- deleted 3y ago[deleted]
- rramadass 3y agoSee also: https://archive.eiffel.com/doc/manuals/technology/oosc/inheritance-design/page.html https://archive.eiffel.com/doc/manuals/technology/oosc/inher...