What is Polymorphism? … To begin to understand this concept, it’s helpful to know that the word polymorphism comes from the Greek meaning many shapes (or forms).  In the context of Object-Oriented Programming, this means the provision of a single interface to entities of different types.  

The interface provided by the objects is the same, but what’s going on under the hood might vary depending on, perhaps, the message sent, or the type passed to a particular function for example.  Typically then the result will be an appropriate type-specific behavior/response.

Ad Hoc Polymorphism (Function/operator overloading)

Ad Hoc Polymorphism is a classification of Polymorphism first defined by Christopher Strachey in 1967). It denotes where a “function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied: (Wikipedia).

Function Overloading

function overloading or method overloading is the ability to create multiple functions of the same name with different implementations. Calls to an overloaded function will run a specific implementation of that function appropriate to the context of the call, allowing one function call to perform different tasks depending on context.

For example, doTask() and doTask(object O) are overloaded functions. To call the latter, an object must be passed as a parameter, whereas the former does not require a parameter, and is called with an empty parameter field. A common error would be to assign a default value to the object in the second function, which would result in an ambiguous call error, as the compiler wouldn’t know which of the two methods to use.

Another appropriate example would be a Print(object O) function. In this case one might like the method to be different when printing, for example, text or pictures. The two different functions may be overloaded as Print(text_object T); Print(image_object P). If we write the overloaded print functions for all objects our program will “print”, we never have to worry about the type of the object, and the correct function call again, the call is always: Print(something)” (Wikipedia).

Operator Overloading

A very simple example of ad hoc polymorphism with operator overloading is the use of the “+” Symbol to denote adding two Integers when it is Ints that are passed in, or alternatively, that same plus symbol will also act to concatenate two strings when Strings are either side of the operator.

Subtype Polymorphism

Subtype polymorphism refers to where one class or type, a subclass (or subtype) inherits the properties and behaviors of another its superclass (or supertype). This means that program elements like “subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype” (Ref#: G). 

Inheritance vs Polymorphism 

  • Inheritance is a mechanism for allowing existing code to be reused in a program.
  • Polymorphism is a mechanism to dynamically decide what form of a function should be invoked.

Polymorphism allows the expression of common behavior between types of objects which have similar traits. Inheritance can be thought of as one of the mechanisms we can use to achieve polymorphism in our code via the use of a hierarchy. However there are other ways to achieve polymorphism besides inheritance, ways which move away from the old OOP ways towards a protocol based approach, and we will explore these other forms of polymorphism later in the article (Ref#: H).

Compile-Time vs Run-Time Polymorphism

Indeed, there are flavors of polymorphism:

Static Polymorphism means the ability to have several functions, all called the same thing, which the compiler can choose between at Compile-Time depending on the arguments passed, and achieved through method overloading (decisions area made at compile-time via static or early binding). It can also mean that we can get different method implementations by casting the same object to different types.

In contrast, Run-Time or Dynamic Polymorphism is polymorphism that happens at run-time and it achieved via method overriding (though dynamic or late binding). “…the system has to run the program and, when a decision needs to be made about which method to run, it must look at the actual object in memory (regardless of the type of the reference, which may be a cast) and act appropriately” (Ref#: N).

(i) Most programmers use the word ‘polymorphism’ to refer to dynamic polymorphism.

Swift Code Example A: Static Polymorphism

In the above code sample, we have created two different functions with the same name “drive()”. Yet we can send different parameter types to drive using the exact same syntax, and the compiler will automatically select for us the correct function to execute based on the type it detects being passed in, and so we don’t need to remember a bunch of different function names like driveVan, driveTractor but can take advantage of this layer of abstraction to make things easier.

Swift Code Example B: Dynamic Polymorphism

Say you start with a class Vehicle to refer to any vehicle; you can then create a class Car which inherits from Vehicle. In this case, the class Car has all the same interface elements as Vehicle but perhaps with some extra methods or variables as well (it can extend its superclass). Car can also override functions it inherits from its superclass, replacing the behavior inside that same-named function in the superclass but maintaining the same interface

Screen Shot 2018-03-22 at 18.48.25.pngscreen-shot-2018-03-22-at-18-48-45.png

What going on here is that:  1. we first have crated a struct called Driver and given it two required elements of name and age  2. we create a class called Vehicle and give it a function called drive() and custom init into which we must inject a Driver to create an instance of vehicle 3. we subclass Vehicle with a class called Car (using the syntax class ClassName: SuperClass {…}) then we choose to override the drive function to customize the implementation whilst leaving the interface the same  4. now we create an instance of our struct Driver and inject it into an instance of Car called car. When we call car.drive() we see the output of “Dave has started to drive a Car!” 5.  we look to use the Type Casting (coercion) features of Swift to cast / upcast our car (which has the type of  Car) as type Vehicle, it’s superclass, we would not, however, be able to downcast in this case (with this type of subsumption we need to have in SML a t1 <: t2 subtyping relation to allow any M1 : t1 to be used as M1 : t2 without violating safety).

Linking to SOLID Principles

Thinking about our SOLID principles, one crucial principle here, one inadvertently popularized by Barbara Liskov in a talk she once gave, is Liskov’s Substitution Principle. This principle states that if S is a subtype of T, then objects of type T should be substitutable with objects of type S without altering or changing any of the desirable properties that T may have. In other words that objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program. If we find ourselves breaking this principle then we have probably failed to correctly identify the right set of abstractions we should be using. 

Parametric Polymorphism & Generics

Parametric Polymorphism is when your code is written without reference to any specific type, and for that reason, it can be used transparently with a range of different types.  Parametric Polymorphism enables you to reuse designs for subprograms (such as standard algorithms for sorting) and also for Abstract Data Types (ADTs), and this is because a generic software component is one that is parameterised with respect to a type T on which it depends, it can be instantiated whenever we want to make an ordinary (non-generic) software component. Therefore, this approach is most often called generics, but in the world of functional programming, it can be referred to just using the term polymorphism. Thus, we can see that with Generics, a function or an ADT can be written generically which means it can handle a range of values irrespective of their type.

In Swift, we are able to use generics with Classes, Enums, Structs, Initializers, and Functions.

Simple Example

(Source: Ref# M)

Ada Examples

To understand how this plays out in other programming languages let’s look at an example from Ada.

Generic Subprograms

ETC

Generic Packages

ETC

Type Safety

Generics may also be used to increase type safety by setting expected types during declaration (as these expected types might be specified to be those which adhere to a particular protocol like Comparable). Types that conform to the Comparable protocol in Swift are types that can be compared using the relational operators <, <=, >=, and >, or where one element of that type might be compared with another using these operators

Generic Parameter Clause

The way we set the expected types during the declaration is using a “Generic Parameter Clause”:

A Generic Parameter Clause is a comma-separated list of generic parameters enclosed in angle brackets <> with each generic parameter which is inside these angle bracket taking the form:

type parameter: constraint

  • Where the type parameter is simply the name of a placeholder type (for example, T, U, V or any word beginning with a capital letter denoting a generic placeholder type).
  • The constraint is a reference to a type parameter which inherits from a specific class or conforms to a particular protocol (like Comparable) or protocol composition (Ref# F).

So, therefore, using a Generic Parameter Clause will tend to look something like the below example.

The above example assumes the existence also of a merge function: demonstrated here.

Generic *type parameters* should be given descriptive, upper camel case names. However, when a type name doesn’t have a meaningful relationship or role, then use a  **single uppercase letter like T, U, or V**. Giving us syntax like the below where we use just T for something that doesn’t particularly hold a meaning relationship or role, but we use Element for something which does.

Preferred

Not Preferred

Summary

In conclusion, Polymorphism is a super powerful tool for us to use in our programs. In this article, we have seen what polymorphism means in the context of OO programming and different ways this can be used. We have also discovered that there are several main classifications of polymorphism:

  • Ad Hoc Polymorphism
  • Subtype Polymorphism
  • Parametric Polymorphism

We found out that we can distinguish between static (compile-time) and dynamic (run-time) polymorphism.

We discovered that the advantages of polymorphism include making our code more reusable, and flexible, it can also simplify “the programming interface [permitting] conventions to be established that can be reused in class after class. Instead of inventing a new name for each new function you add to a program, the same names can be reused. The programming interface can be described as a set of abstract behaviors, quite apart from the classes that implement them.” (SOURCE: A)

Please add comments to suggest any improvements to the wording or accuracy of any of these posts. Thanks.
Most Recent Edits: 21/08/2019

References

A: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/OOP_ObjC/Articles/ooObjectModel.html

B: https://en.wikipedia.org/wiki/Parametric_polymorphism

C: https://rosettacode.org/wiki/Parametric_polymorphism

D: http://www.tutorialspoint.com/objective_c/objective_c_polymorphism.htm

E: Miller, B. J. (2016). Sams Teach Yourself Swift: Second Edition. SAMS, USA.

F: “Generic Parameters and Arguments”. Sourced from: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GenericParametersAndArguments.html#//apple_ref/doc/uid/TP40014097-CH37-ID406

G: https://en.wikipedia.org/wiki/Subtyping

H: https://techdifferences.com/difference-between-inheritance-and-polymorphism.html

I: https://www.programiz.com/swift-programming/function-overloading

J: http://www.cl.cam.ac.uk/teaching/1617/Types/lectures/lecture-1.pdf

K: https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system

L: http://www.uio.no/studier/emner/matnat/ifi/INF3110/h05/lysark/Types.pdf

M: https://www.letsbuildthatapp.com/blog/Swift:-The-Power-of-Generics

N: https://www.cl.cam.ac.uk/teaching/1819/OOProg/handout.pdf

Loading

Last modified: August 20, 2022

Author

Comments

Write a Reply or Comment

Your email address will not be published.