[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)