Polymorphism |
---|
Ad hoc polymorphism |
Parametric polymorphism |
Subtyping |
In programming languages, ad hoc polymorphism [1] is a kind of polymorphism in which polymorphic functions can be applied to arguments of different types, because a polymorphic function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied. When applied to object-oriented or procedural concepts, it is also known as function overloading or operator overloading. The term ad hoc in this context is not intended to be pejorative; it refers simply to the fact that this type of polymorphism is not a fundamental feature of the type system. This is in contrast to parametric polymorphism, in which polymorphic functions are written without mention of any specific type, and can thus apply a single abstract implementation to any number of types in a transparent way. This classification was introduced by Christopher Strachey in 1967.
Ad hoc polymorphism is a dispatch mechanism: control moving through one named function is dispatched to various other functions without having to specify the exact function being called. Overloading allows multiple functions taking different types to be defined with the same name; the compiler or interpreter automatically ensures that the right function is called. This way, functions appending lists of integers, lists of strings, lists of real numbers, and so on could be written, and all be called append—and the right append function would be called based on the type of lists being appended. This differs from parametric polymorphism, in which the function would need to be written generically, to work with any kind of list. Using overloading, it is possible to have a function perform two completely different things based on the type of input passed to it; this is not possible with parametric polymorphism (but would have to be achieved with switching on the type inside the generic function). Another way to look at overloading is that a routine is uniquely identified not by its name, but by the combination of its name and the number, order and types of its parameters.
This type of polymorphism is common in object-oriented programming languages, many of which allow operators to be overloaded in a manner similar to functions (see operator overloading). Some languages that are not dynamically typed and lack ad hoc polymorphism (including type classes) have longer function names such as print_int
, print_string
, etc. This can be seen as advantage (more descriptive) or a disadvantage (overly verbose) depending on one's point of view.
An advantage that is sometimes gained from overloading is the appearance of specialization, e.g., a function with the same name can be implemented in multiple different ways, each optimized for the particular data types that it operates on. This can provide a convenient interface for code that needs to be specialized to multiple situations for performance reasons. The downside is that the type system cannot guarantee the consistency of the different implementations.
Since overloading is done at compile time, it is not a substitute for late binding as found in subtyping polymorphism.
The previous section notwithstanding, there are other ways in which ad hoc polymorphism can work out. Consider for example the Smalltalk language. In Smalltalk, the overloading is done at run time, as the methods ("function implementation") for each overloaded message ("overloaded function") are resolved when they are about to be executed. This happens at run time, after the program is compiled. Therefore, polymorphism is given by subtyping polymorphism as in other languages, and it is also extended in functionality by ad hoc polymorphism at run time.
A closer look will also reveal that Smalltalk provides a slightly different variety of ad hoc polymorphism. Since Smalltalk has a late bound execution model, and since it provides objects the ability to handle messages that are not understood, it is possible to implement functionality using polymorphism without explicitly overloading a particular message. This may not be generally recommended practice for everyday programming, but it can be quite useful when implementing proxies.
Also, while in general terms common class method and constructor overloading is not considered polymorphism, there are more uniform languages in which classes are regular objects. In Smalltalk, for instance, classes are regular objects. In turn, this means messages sent to classes can be overloaded, and it is also possible to create objects that behave like classes without their classes inheriting from the hierarchy of classes. These are effective techniques that can be used to take advantage of Smalltalk's powerful reflection capabilities. Similar arrangements are also possible in languages such as Self and Newspeak.
Imagine an operator +
that may be used in the following ways:
1 + 2 = 3
3.14 + 0.0015 = 3.1415
1 + 3.7 = 4.7
[1, 2, 3] + [4, 5, 6] = [1, 2, 3, 4, 5, 6]
[true, false] + [false, true] = [true, false, false, true]
"bab" + "oon" = "baboon"
To handle these six function calls, four different pieces of code are needed (or three, if strings are considered to be lists of characters):
Thus, the name +
actually refers to three or four completely different functions. This is an example of overloading or more specifically, operator overloading .
Note the ambiguity in the string types used in the last case. Consider "123" + "456"
in which the programmer might naturally assume addition rather than concatenation. They may expect "579"
instead of "123456"
. Overloading can therefore provide different meaning, or semantics, for an operation, as well as differing implementations.
In computer programming, operator overloading, sometimes termed operator ad hoc polymorphism, is a specific case of polymorphism, where different operators have different implementations depending on their arguments. Operator overloading is generally defined by a programming language, a programmer, or both.
Polymorphism, polymorphic, polymorph, polymorphous, or polymorphy may refer to:
Smalltalk is a purely object oriented programming language (OOP) that was originally created in the 1970s for educational use, specifically for constructionist learning, but later found use in business. It was created at Xerox PARC by Learning Research Group (LRG) scientists, including Alan Kay, Dan Ingalls, Adele Goldberg, Ted Kaehler, Diana Merry, and Scott Wallace.
C++ is a high-level, general-purpose programming language created by Danish computer scientist Bjarne Stroustrup. First released in 1985 as an extension of the C programming language, it has since expanded significantly over time; as of 1997, C++ has object-oriented, generic, and functional features, in addition to facilities for low-level memory manipulation for systems like microcomputers or to make operating systems like Linux or Windows. It is usually implemented as a compiled language, and many vendors provide C++ compilers, including the Free Software Foundation, LLVM, Microsoft, Intel, Embarcadero, Oracle, and IBM.
In computer programming, a generic function is a function defined for polymorphism.
A method in object-oriented programming (OOP) is a procedure associated with an object, and generally also a message. An object consists of state data and behavior; these compose an interface, which specifies how the object may be used. A method is a behavior of an object parametrized by a user.
In programming language theory and type theory, polymorphism is the use of a single symbol to represent multiple different types.
In some programming languages, 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.
The Glasgow Haskell Compiler (GHC) is a native or machine code compiler for the functional programming language Haskell. It provides a cross-platform software environment for writing and testing Haskell code and supports many extensions, libraries, and optimisations that streamline the process of generating and executing code. GHC is the most commonly used Haskell compiler. It is free and open-source software released under a BSD license. The lead developers are Simon Peyton Jones and Simon Marlow.
In a given programming language design, a first-class citizen is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, and assigned to a variable.
In computer science, dynamic dispatch is the process of selecting which implementation of a polymorphic operation to call at run time. It is commonly employed in, and considered a prime characteristic of, object-oriented programming (OOP) languages and systems.
In computer programming, operators are constructs defined within programming languages which behave generally like functions, but which differ syntactically or semantically.
In software engineering, 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. In most object-oriented systems, the concrete function that is called from a function call in the code depends on the dynamic type of a single object and therefore they are known as single dispatch calls, or simply virtual function calls.
In programming languages and type theory, parametric polymorphism allows a single piece of code to be given a "generic" type, using variables in place of actual types, and then instantiated with particular types as needed. Parametrically polymorphic functions and data types are sometimes called generic functions and generic datatypes, respectively, and they form the basis of generic programming.
In computer science, a type class is a type system construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types. Such a constraint typically involves a type class T
and a type variable a
, and means that a
can only be instantiated to a type whose members support the overloaded operations associated with T
.
A structural type system is a major class of type systems in which type compatibility and equivalence are determined by the type's actual structure or definition and not by other characteristics such as its name or place of declaration. Structural systems are used to determine if types are equivalent and whether a type is a subtype of another. It contrasts with nominative systems, where comparisons are based on the names of the types or explicit declarations, and duck typing, in which only the part of the structure accessed at runtime is checked for compatibility.
In programming language theory, parametricity is an abstract uniformity property enjoyed by parametrically polymorphic functions, which captures the intuition that all instances of a polymorphic function act the same way.
Inline caching is an optimization technique employed by some language runtimes, and first developed for Smalltalk. The goal of inline caching is to speed up runtime method binding by remembering the results of a previous method lookup directly at the call site. Inline caching is especially useful for dynamically typed languages where most if not all method binding happens at runtime and where virtual method tables often cannot be used.
In type theory, bounded quantification refers to universal or existential quantifiers which are restricted ("bounded") to range only over the subtypes of a particular type. Bounded quantification is an interaction of parametric polymorphism with subtyping. Bounded quantification has traditionally been studied in the functional setting of System F<:, but is available in modern object-oriented languages supporting parametric polymorphism (generics) such as Java, C# and Scala.
In type theory, an intersection type can be allocated to values that can be assigned both the type and the type . This value can be given the intersection type in an intersection type system. Generally, if the ranges of values of two types overlap, then a value belonging to the intersection of the two ranges can be assigned the intersection type of these two types. Such a value can be safely passed as argument to functions expecting either of the two types. For example, in Java the class Boolean
implements both the Serializable
and the Comparable
interfaces. Therefore, an object of type Boolean
can be safely passed to functions expecting an argument of type Serializable
and to functions expecting an argument of type Comparable
.