[HN Gopher] Polymorphism and other important aspects of inheritance
       ___________________________________________________________________
        
       Polymorphism and other important aspects of inheritance
        
       Author : ingve
       Score  : 31 points
       Date   : 2023-07-10 06:47 UTC (16 hours ago)
        
 (HTM) web link (utcc.utoronto.ca)
 (TXT) w3m dump (utcc.utoronto.ca)
        
       | eterps wrote:
       | The Oberon programming language supports inheritance and/or
       | polymorphism on different levels of abstraction:
       | 
       | http://miasap.se/obnc/data-abstraction.html
        
       | jrochkind1 wrote:
       | I 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 wrote:
         | With nowadays approach where usually people favor composition
         | over inheritance all is true that OO is better without
         | inheritance.
        
       | david2ndaccount wrote:
       | Note 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>, search for "Extensions".
        
         | nsajko wrote:
         | I think GCC also supports this for a long time already, see
         | -fplan9-extensions and -fms-extensions
        
       | CSMastermind wrote:
       | Perhaps 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 wrote:
         | I 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 wrote:
           | This 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 wrote:
         | None of those Person/Employee examples are pure OOP, FWIW.
         | 
         | * https://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented
         | 
         | * 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 wrote:
           | Bringing 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 wrote:
         | The 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 wrote:
           | What 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?
        
       | [deleted]
        
       | hazelnut-tree wrote:
       | Polymorphism 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
       | 
       | > 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 wrote:
       | For 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 wrote:
         | You'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.
        
       | kleiba wrote:
       | Clojure has spent a considerable amount of design work on
       | polymorphism: https://clojure.org/about/runtime_polymorphism
        
       | coding123 wrote:
       | Two 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.
        
         | zwieback wrote:
         | > 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).
         | 
         | Also: class hierarchies don't have to match real-life
         | hierarchies. Often I find it more useful to implement
         | hierarchies that make the program flow and algorithms work more
         | smoothly and accept hierarchies that don't mirror anything in
         | the physical world.
        
         | commandlinefan wrote:
         | > 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.
        
           | zwieback wrote:
           | Unless Human already existed and you want to inherit the
           | functionality without retyping stuff or delegating. I think
           | that's where the old "prefer delegation over inheritance"
           | advice comes in but I'm not against convenience-inheritance.
        
       | pdpi wrote:
       | The 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.
        
       ___________________________________________________________________
       (page generated 2023-07-10 23:01 UTC)