← Back

Polymorphism

In computing, an interface is a shared boundary across which two or more separate components of a computer system exchange information.

Software Interface

Interfaces exist for various ] of software such as functions, objects, programs, OS, etc.

A function's interface is the combination of it's name, arguments' type arity and return type.

Polymorphism

Polymorphism is sharing an interface for different types. Types of arguments passed and expected return value may determine the actual implementation to execute.

Parametric Polymorphism

Parametric polymorphism allows a single piece of code to be typed generically, using variables in place of actual types, and then instantiated with particular types as needed. Parametric definitions are uniform: all their instances behave the same.

Ad-hoc Polymorphism

Ad-hoc polymorphism, by contrast, allows a polymorphic value to exhibit different behaviors when “viewed” at different types. The most common example of ad-hoc polymorphism is overloading, which associates a single function symbol with many implementations; the compiler chooses an appropriate implementation for each application of the function, based on the types of the arguments.

Haskell uses typeclasses for ad-hoc polymorphism.

Subtyping

Extending an interface is called subtyping. The parent interface is called supertype. Anything that implements subtype also implicity implements the supertype and is substitutable for the supertype. This is a way to reuse interfaces for ad-hoc polymorphisms.

Virtual Function

A virtual function is an inheritable and overridable function or method that is dispatched dynamically. Virtual functions are an important part of runtime polymorphism. They allow for the execution of target functions that were not precisely identified at compile time.

Dynamic Dispatch

Dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time.

Late binding refers to the object-side of an eval, dynamic dispatch refers to the functional-side. In late binding the type of a variable is the variant at runtime. In dynamic-dispatch, the function or subroutine being executed is the variant.

Binding in general refers to resolving a symbol to some value/object/implementation. For example, an assignment statement x = 42 binds the symbol x to the value of the expression 42. Type names can always be resolved during compile time in Java.

Dispatch (or more precisely call dispatch or method dispatch) is the kind of binding that resolves a method call (or function call) to a method implementation.

Double dispatch is a special form of multiple dispatch, and a mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call.

Function overloading, however, is done at compile time using name mangling where the internal name of the function encodes the argument's type. For example, a function foo(int) may internally be called __foo_i.

Variance

Variance is how subtyping between more complex types relates to subtyping between their components. For example, how should a list of Cats relate to a list of Animals? Or how should a function that returns Cat relate to a function that returns Animal

Depending on the variance of the type constructor, the subtyping relation of the simple types may be either preserved, reversed, or ignored for the respective complex types.

A programming language designer will consider variance when devising typing rules for language features such as arrays, inheritance, and generic datatypes. By making type constructors covariant or contravariant instead of invariant, more programs will be accepted as well-typed.