Dr Seuss on Polymorphism
Tags: polymorphism, typescript, scala
Published: 2024-04-28 20:20:17

# Polymorphism: plug-n-playability

Polymorphism, it's a plug and play delight,
Where objects morph to fit just right.
Like a versatile plug in a socket's embrace,
In different methods, they find their place.

No need to fret about type or shape,
Polymorphism ensures there's no red tape.
With flexibility and ease, it's always at hand,
In the coding world, where wonders expand.


## Kinds of polymorphism
   * [Subtyping polymorphism](#subtyping-polymorphism)
   * [Ad-hoc polymorphism or method overloading](#ad-hoc-polymorphism-or-method-overloading)
   * [Parametric polymorphism or Generics](#parametric-polymorphism-or-generics)
   * [Row polymorphism or better said: if walks like a duck and quacks like a duck it's duck typing](#row-polymorphism-or-better-said-if-walks-like-a-duck-and-quacks-like-a-duck-its-duck-typing)



<a name="subtyping-polymorphism"></a>
## Subtyping polymorphism

In the whimsical world of coding, there lived a concept quite grand,
Called *"inheritance polymorphism"* in a far-off coding land.
It's a tale of objects and classes, oh so merry,
Where each bit of code is like a character, quite contrary.

Now, picture a class, a blueprint, you see,
With methods and data, all part of its decree.
But wait! There's a twist, a magical charm,
Inheritance comes along to prevent any harm.

When one class inherits from another, you see,
It gains all the traits, quite effortlessly.
Like a child inheriting traits from their kin,
The new class inherits, it's a wonderful win!

But here's where the tale takes a twist so divine,
Polymorphism enters, oh how it shines!
For you see, a child class can act as its own,
With methods and data, in a world it can roam.

Imagine a Cat, with its meows and its purrs,
Inherits from Animal, with its roars and its stirs.
But when asked to speak, in a language so neat,
The Cat says "Meow," while the Lion, "Roar!" to repeat.

First comes the definition, a blueprint so bright,
With fields and functions, oh what a sight!
But if it inherits, there's more to behold,
As it gains from its parent, stories untold.

Behind the scenes, in a memory land,
Superclasses are initialized, hand in hand.
The constructor is called, to set things aright,
From parent to child, in the soft coding light.

When methods are called, oh what a spree,
The runtime environment searches, from A to Z.
It looks in the subclass, then up it will go,
Through the inheritance chain, to put on a show.

And when it finds the method, a joyous display,
It dispatches and calls it, without delay.
Polymorphism reigns, as objects align,
Inheritance's magic, truly divine.

But don't forget access, to keep things in line,
With public and private, oh so fine.
Encapsulation rules, with a watchful eye,
To protect our code, as it dances in the sky.

So remember the tale, of inheritance grand,
In the land of code, where wonders stand.
With memory, methods, and objects so bright,
Inheritance's story, a true delight!

```ts
// Define a base class Vehicle
class Vehicle {
  constructor(public name: string) {}

  drive(): void {
    console.log(`${this.name} is being driven.`);
  }
}

// Define a subclass Car inheriting from Vehicle
class Car extends Vehicle {
  constructor(name: string) {
    super(name);
  }

  // Additional method specific to Car
  honk(): void {
    console.log(`${this.name} is honking.`);
  }
}

// Define another subclass Plane inheriting from Vehicle
class Plane extends Vehicle {
  constructor(name: string) {
    super(name);
  }

  // Additional method specific to Plane
  fly(): void {
    console.log(`${this.name} is flying.`);
  }
}

// Create instances of subclasses and demonstrate inheritance
const myCar = new Car("Toyota");
const myPlane = new Plane("Boeing");

myCar.drive();  // Output: Toyota is being driven.
myCar.honk();   // Output: Toyota is honking.

myPlane.drive(); // Output: Boeing is being driven.
myPlane.fly();   // Output: Boeing is flying.

```


<a name="ad-hoc-polymorphism-or-method-overloading"></a>
## Ad-hoc polymorphism or method overloading

In the land of coding, oh what a sight,
There's a concept called polymorphism, shining so bright.
But wait, there's a twist, a special kind of charm,
It's ad hoc polymorphism, in the coding farm.

Now, let me tell you a tale, oh so merry,
Of ad hoc polymorphism, oh how it's airy.
Imagine you have a function, simple and neat,
But it can act differently, depending on its seat.

You see, ad hoc polymorphism is quite unique,
It's like a shape-shifting technique.
The same function can do this or that,
Based on the types it's given, imagine that!

One moment it's adding numbers, oh so fine,
The next it's concatenating strings, in a line.
It adapts and adjusts, with such grace,
No need for inheritance, in this coding space.

It's like a wizard with spells, so clever,
Changing its form, and doing it ever.
With <u>typeclasses or overloading</u>, it's quite a show,
Ad hoc polymorphism, stealing the glow.

So in the land of coding, where wonders thrive,
Ad hoc polymorphism, keeps the code alive.
It's flexibility and power, a sight to behold,
In the whimsical world of coding, oh so bold!

now some examples:

I'm going to use scala here as it supports both methods natively.

method overloading:

```scala
// Define a class Calculator
class Calculator {
  // Method to add integers
  def add(a: Int, b: Int): Int = a + b
  
  // Method to concatenate strings
  def add(a: String, b: String): String = a + b
}

// Create an instance of Calculator
val calculator = new Calculator()

// Use method overloading to demonstrate ad hoc polymorphism
val sumOfIntegers = calculator.add(5, 7)
val concatenatedStrings = calculator.add("Hello, ", "world!")

println(s"Sum of integers: $sumOfIntegers")           // Output: Sum of integers: 12
println(s"Concatenated strings: $concatenatedStrings") // Output: Concatenated strings: Hello, world!

```

using typeclasses:

```scala
// Define a type class Addable
trait Addable[T] {
  def add(a: T, b: T): T
}

// Define instances of Addable for Int and String
implicit object AddableInt extends Addable[Int] {
  def add(a: Int, b: Int): Int = a + b
}

implicit object AddableString extends Addable[String] {
  def add(a: String, b: String): String = a + b
}

// Define a function to add values of any type T using the Addable type class
def add[T](a: T, b: T)(implicit addable: Addable[T]): T = addable.add(a, b)

// Use type classes to demonstrate ad hoc polymorphism
val sumOfIntegers = add(5, 7)
val concatenatedStrings = add("Hello, ", "world!")

println(s"Sum of integers: $sumOfIntegers")           // Output: Sum of integers: 12
println(s"Concatenated strings: $concatenatedStrings") // Output: Concatenated strings: Hello, world!

```


<a name="parametric-polymorphism-or-generics"></a>
## Parametric polymorphism or Generics

In the land of coding, there's a polymorphic twist,
A concept called parametric, that you shouldn't miss.
It's a tale of generics, oh so grand,
Where types are flexible, like grains of sand.

Parametric polymorphism, let me explain,
It's like a magic spell, with a wide domain.
Instead of a specific type, you see,
We use a placeholder, for any degree.

With parametric polymorphism, the function's quite slick,
It works for any type, like a clever trick.
No need to rewrite, for each new type that we find,
One function fits all, it's truly refined.

Imagine a box, that holds treasures untold,
With parametric polymorphism, its secrets unfold.
It can hold numbers, strings, or anything at all,
With a single function call, it answers the call.

So if you seek flexibility, in your coding quest,
Parametric polymorphism, is surely the best.
With generics and types, it opens the door,
To a world of possibilities, forevermore.


in scala:
```scala
// Define a generic function that works with any type T
def printElement[T](element: T): Unit = {
  println(element)
}

// Using the generic function with different types
printElement(10)         // Output: 10
printElement("Hello")    // Output: Hello
printElement(3.14)       // Output: 3.14

```

in typescript:
```typescript
// Define a generic function that works with any type T
function printElement<T>(element: T): void {
  console.log(element);
}

// Using the generic function with different types
printElement(10);        // Output: 10
printElement("Hello");   // Output: Hello
printElement(3.14);      // Output: 3.14

```


<a name="row-polymorphism-or-better-said-if-walks-like-a-duck-and-quacks-like-a-duck-its-duck-typing"></a>
## Row polymorphism or better said: if walks like a duck and quacks like a duck it's duck typing

In the coding realm, there's a type tale to be told,
Of row polymorphism, a story quite bold.
It's a whimsical world where types are in sync,
By the structure they hold, not just what they think.

Imagine two creatures, a person and a pet,
With names and with ages, a pair you won't forget.
Though their labels may differ, their essence is the same,
In the land of row polymorphism, they play the same game.

A person named Alice, with an age quite spry,
And Fluffy the pet, with a sparkle in his eye.
Though one's called "name" and the other "petName",
Their structures align, like a well-played game.

Now enters a function, quite clever and bright,
It prints out their info, with all its might.
It cares not for names, nor the labels they wear,
Only the structure they bear, with a flair.

So Alice and Fluffy, they step up to the plate,
With their different names, yet the function can relate.
For row polymorphism, it's all about fit,
By structure, not name, it's a magical writ.

In this whimsical world, where types dance and rhyme,
Row polymorphism shines, every single time.
So remember this tale, as you code and you craft,
In the land of row polymorphism, it's structure that laughs!

```typescript
// Define a type representing a person
type Person = {
  name: string;
  age: number;
};

// Define a type representing a pet
type Pet = {
  petName: string;
  petAge: number;
};

// Define a function that accepts any object with a name and age property
function printInfo(obj: { name: string; age: number }): void {
  console.log(`Name: ${obj.name}, Age: ${obj.age}`);
}

// Create objects with different property names but the same structure
const person: Person = { name: "Alice", age: 30 };
const pet: Pet = { petName: "Fluffy", petAge: 5 };

// Both person and pet objects can be passed to the printInfo function
printInfo(person); // Output: Name: Alice, Age: 30
printInfo(pet);    // Output: Name: Fluffy, Age: 5

```