All Articles

Polymorphism

Polymorphism allows a function to change its behaviour depending on the type of its arguments. There are three kinds of Polymorphism which are commonly used.

Parametric Polymorphism

This kind of polymorphism is usually known generic polymorphism. The following Haskell example illustrates the use of generic types using the concatenation (++) operator.

> :t (++)
(++) :: [a] -> [a] -> [a]

> "Hello" ++ " World" 
"Hello World"

> [1,2,3] ++ [4,5,6] 
[1,2,3,4,5,6]

The signature for (++) has a type variable a which symbolizes any type possible, also known as the generic. The operator takes in two homogenous lists of type a, and returns a homogenous list of type a.

Ad Hoc Polymorphism

Ad Hoc Polymorphism is most commonly implemented as function overloading. Suppose there are two different classes with the same function name.

class Dog {
    func speak() -> String {
        return "Woof"
    }
}

class Cat {
    func speak() -> String {
        return "Meow"
    }
}

Using function overloading, the same function name can be reused to call speak in either instance.

func talk(speaker: Cat) {
    speaker.speak()
}

func talk(speaker: Dog) {
    speaker.speak()
}

In this example, two talk functions are declared, the only difference is the type that each accepts. Whenever either function is used, Swift will know which function to use based on the type of the speaker.

let cat = Cat()
talk(cat) // Meow

let dog = Dog()
talk(dog) // Woof

This example can be further simplified by using Parametric Polymorphism by creating a Swift Protocol which can be used as a generic type. This is similar to a typeclass in Haskell.

By using a Protocol that enforces a speak function, Dog and Cat can be referred to in more broader terms.

protocol Speaker {
    func speak () -> String
}

class Dog: Speaker {
    func speak() -> String {
        return "Woof"
    }
}

class Cat: Speaker {
    func speak() -> String {
        return "Meow"
    }
}

Which means that only one talk function is needed to make dogs and cats speak.

func talk(speaker: Speaker) {
    speaker.speak()
}

Subtype Polymorphism

In object-oriented programming, the most common form of polymorphism is inheritance, this is an example of subtype polymorphism. The previous example can be rewritten using inheritance.

class Speaker {
    var speech = ""
    
    func speak() -> String {
        return speech
    }
}

class Dog: Speaker {
    override init() {
        super.init()
        speech = "Woof"
    }
}

class Cat: Speaker {
    override init() {
        super.init()
        speech = "Meow"
    }
}

By introducing the Speaker class, Dog and Cat can inherit the speak from their superclass. Similarly, the instances of cat and dog can be passed into talk as type variables.

func talk(speaker: Speaker) {
    speaker.speak()
}

let cat = Cat()
talk(cat) // Meow

let dog = Dog()
talk(dog) // Woof

This fragment of the code will continue to work, since Cat and Dog are of type Speaker via subtyping. Though using Protocol is just as valid.

Summary

Parametric polymorphism allows generic types in functions. Ad hoc polymorphism allows function overloading to support multiple types. Subtype polymorphism allows functions to be of a certain type, which can then be passed into a generic or overloaded function.

In either case, polymorphism allows functions to support more kinds of inputs without having to worry about individual type implementations.

Published 9 Dec 2015