| advertise add site services publishers database health videos | ![]() | about toolbar stats live show health store more stuff JOIN/LOGIN |
Generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters. This approach, pioneered by Ada in 1983, permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplication. Software entities created using generic programming are known as generics in Ada, Eiffel, Java, C#, Visual Basic .NET and Haskell; templates in C++; and parameterized types in the influential 1994 book Design Patterns. The authors of Design Patterns note that this technique, especially when combined with delegation, is very powerful but that "Dynamic, highly parameterized software is harder to understand than more static software." (Gang of Four 1995:21) Generic programming refers to features of certain statically typed programming languages that allow some code to effectively circumvent the static typing requirements. For instance in C++, a template is a routine in which some parameters are qualified by a type variable. Since code generation in C++ depends on concrete types, the template is specialized for each combination of argument types that occur in practice. Generic programming is commonly used to implement containers such as lists and hash tables and functions such as a particular sorting algorithm for objects specified in terms more general than a concrete type. [edit] Programming language support for generic programmingGeneric programming facilities first appeared in the 1970s[dubious ] in languages like CLU and Ada, and were subsequently adopted by many object-based and object-oriented languages, including BETA, C++, D, Eiffel, Java, and DEC's now defunct Trellis-Owl language. Implementations of generics in languages such as Java and C# are formally based on the notion of parametricity, due to John C. Reynolds. Generic programming is implemented and supported differently within each language. The term "generic" has also been used differently in programming contexts. For example, in Forth the compiler can execute code while compiling and one can create new compiler keywords and new implementations for those words on the fly. It has few words that expose the compiler behaviour and therefore naturally offers generic programming capacities which, however, are not referred to as such in most Forth texts. The term has been used in functional programming, specifically in Haskell-like languages, which use a structural type system where types are always parametric and the actual code on those types is generic. In these uses generic programming still serves the similar purpose of code-saving and the rendering of an abstraction. [edit] Generic programming in object-oriented languagesWhen creating containers of objects it is possible to write specific implementations for each datatype contained, even if the code is virtually identical except for different datatypes. In C++, this duplication of code can be circumvented by defining a template class: template<typename T> class List { /* class contents */ }; List<Animal> list_of_animals; List<Car> list_of_cars; Above, T is a placeholder for whatever type is specified when the list is created. These "containers-of-type-T", commonly called generics, are a generic programming technique allowing a class to be reused with different datatypes as long as certain contracts such as subtypes and signature are kept. Generic programming should not be confused with inclusion polymorphism, which is the algorithmic usage of exchangeable sub-classes: for instance, a list of objects of type Moving_Object containing objects of type Animal and Car. Generics can also be used for type-independent functions as in the template<typename T> void Swap(T & a, T & b) //"&" passes parameters by reference { T temp = b; b = a; a = temp; } string hello = "world!", world = "Hello, "; Swap( world, hello ); cout << hello << world << endl; //Output is "Hello, world!" The C++ C# 2.0, Chrome 1.5 and Visual Basic .NET 2005 have constructs that take advantage of the support for generics present in the Microsoft .NET Framework since version 2.0. The ML family of programming languages encourage generic programming through parametric polymorphism and generic modules called functors. The type class mechanism of Haskell supports generic programming. Dynamic typing, such as is featured in Objective-C, and, judicious use of protocols circumvent the need for use of generic programming techniques, since there exists a general type to contain any object. While Java does so also, the casting that needs to be done breaks the discipline of static typing, and generics are one way of achieving some of the benefits of dynamic typing with the advantages of having static typing. [edit] Generics in AdaAda has had generics since it was first designed in 1977–1980. The standard library uses generics to provide many services. Ada 2005 adds a comprehensive generic container library to the standard library, which was inspired by C++'s standard template library. A generic unit is a package or a subprogram that takes one or more generic formal parameters. A generic formal parameter is a value, a variable, a constant, a type, a subprogram, or even an instance of another, designated, generic unit. For generic formal types, the syntax distinguishes between discrete, floating-point, fixed-point, access (pointer) types, etc. Some formal parameters can have default values. To instantiate a generic unit, the programmer passes actual parameters for each formal. The generic instance then behaves just like any other unit. It is possible to instantiate generic units at run-time, for example inside a loop. [edit] Ada exampleThe specification of a generic package: generic Max_Size : Natural; -- a generic formal value type Element_Type is private; -- a generic formal type; accepts any nonlimited type package Stacks is type Size_Type is range 0 .. Max_Size; type Stack is limited private; procedure Create (S : out Stack; Initial_Size : in Size_Type := Max_Size); procedure Push (Into : in out Stack; Element : in Element_Type); procedure Pop (From : in out Stack; Element : out Element_Type); Overflow : exception; Underflow : exception; private subtype Index_Type is Size_Type range 1 .. Max_Size; type Vector is array (Index_Type range <>) of Element_Type; type Stack (Allocated_Size : Size_Type := 0) is record Top : Index_Type; Storage : Vector (1 .. Allocated_Size); end record; end Stacks; Instantiating the generic package: type Bookmark_Type is new Natural; -- records a location in the text document we are editing package Bookmark_Stacks is new Stacks (Max_Size => 20, Element_Type => Bookmark_Type); -- Allows the user to jump between recorded locations in a document Using an instance of a generic package: type Document_Type is record Contents : Ada.Strings.Unbounded.Unbounded_String; Bookmarks : Bookmark_Stacks.Stack; end record; procedure Edit (Document_Name : in String) is Document : Document_Type; begin -- Initialise the stack of bookmarks: Bookmark_Stacks.Create (S => Document.Bookmarks, Initial_Size => 10); -- Now, open the file Document_Name and read it in... end Edit; [edit] Advantages and limitationsThe language syntax allows precise specification of constraints on generic formal parameters. For example, it is possible to specify that a generic formal type will only accept a modular type as the actual. It is also possible to express constraints between generic formal parameters; for example: generic type Index_Type is (<>); -- must be a discrete type type Element_Type is private; -- can be any nonlimited type type Array_Type is array (Index_Type range <>) of Element_Type; In this example, Array_Type is constrained by both Index_Type and Element_Type. When instantiating the unit, the programmer must pass an actual array type that satisfies these constraints. The disadvantage of this fine-grained control is a complicated syntax, but, because all generic formal parameters are completely defined in the specification, the compiler can instantiate generics without looking at the body of the generic. Unlike C++, Ada does not allow specialised generic instances, and requires that all generics be instantiated explicitly. These rules have several consequences:
[edit] Templates in C++C++ uses templates to enable generic programming techniques. The C++ Standard Library includes the Standard Template Library or STL that provides a framework of templates for common data structures and algorithms. Templates in C++ may also be used for template metaprogramming, which is a way of pre-evaluating some of the code at compile-time rather than run-time. Using template specialization, C++ Templates are considered turing complete. [edit] Technical overviewThere are two kinds of templates: function templates and class templates. A function template is a pattern for creating ordinary functions based upon the parameterizing types supplied when instantiated. For example, the C++ Standard Template Library contains the function template template <typename T> T max(T x, T y) { return x < y ? y : x; } Specializations of this function template, instantiations with specific types, can be called just like an ordinary function: cout << max(3, 7); // outputs 7 The compiler examines the arguments used to call int max(int x, int y) { return x < y ? y : x; } This works whether the arguments C++ templates are completely type safe at compile time. As a demonstration, the standard type The second kind of template, a class template, extends the same concept to classes. A class template specialization is a class. Class templates are often used to make generic containers. For example, the STL has a linked list container. To make a linked list of integers, one writes [edit] Template specializationA powerful feature of C++'s templates is template specialization. This allows alternative implementations to be provided based on certain characteristics of the parameterized type that is being instantiated. Template specialization has two purposes: to allow certain forms of optimization, and to reduce code bloat. For example, consider a Unlike function templates, class templates can be partially specialized. That means that an alternate version of the class template code can be provided when some of the template parameters are known, while leaving other template parameters generic. This can be used, for example, to create a default implementation (the primary specialization) that assumes that copying a parameterizing type is expensive and then create partial specializations for types that are cheap to copy, thus increasing overall efficiency. Clients of such a class template just use specializations of it without needing to know whether the compiler used the primary specialization or some partial specialization in each case. Class templates can also be fully specialized, which means that an alternate implementation can be provided when all of the parameterizing types are known. [edit] Advantages and disadvantagesSome uses of templates, such as the #define max(a,b) ((a) < (b) ? (b) : (a)) Both macros and templates are expanded at compile time. Macros are always expanded inline; templates can also be expanded as inline functions when the compiler deems it appropriate. Thus both function-like macros and function templates have no run-time overhead. However, templates are generally considered an improvement over macros for these purposes. Templates are type-safe. Templates avoid some of the common errors found in code that makes heavy use of function-like macros. Perhaps most importantly, templates were designed to be applicable to much larger problems than macros. There are three primary drawbacks to the use of templates: compiler support, poor error messages, and code bloat. Many compilers historically have poor support for templates, thus the use of templates can make code somewhat less portable. Support may also be poor when a C++ compiler is being used with a linker which is not C++-aware, or when attempting to use templates across shared library boundaries. Most modern compilers however now have fairly robust and standard template support, and the new C++ standard, C++0x, is expected to further address these issues. Almost all compilers produce confusing, long, or sometimes unhelpful error messages when errors are detected in code that uses templates.[1] This can make templates difficult to develop. Finally, the use of templates requires the compiler to generate a separate instance of the templated class or function for every permutation of type parameters used with it. (This is necessary because types in C++ are not all the same size, and the sizes of data fields are important to how classes work.) So the indiscriminate use of templates can lead to code bloat, resulting in excessively large executables.[citation needed] However, judicious use of template specialization can dramatically reduce such code bloat in some cases. The extra instantiations generated by templates can also cause debuggers to have difficulty working gracefully with templates. For example, setting a debug breakpoint within a template from a source file may either miss setting the breakpoint in the actual instantiation desired or may set a breakpoint in every place the template is instantiated. Also, because the compiler needs to perform macro-like expansions of templates and generate different instances of them at compile time, the implementation source code for the templated class or function must be available (e.g. included in a header) to the code using it. Templated classes or functions, including much of the Standard Template Library (STL), cannot be compiled. (This is in contrast to non-templated code, which may be compiled to binary, providing only a declarations header file for code using it.) This may be a disadvantage by exposing the implementing code, which removes some abstractions, and could restrict its use in closed-source projects.[citation needed] [edit] Templates in DThe D programming language supports templates that are evolved from those in C++. Most C++ template idioms will carry over to D without alteration, but D adds functionality that streamlines some common cases. The most obvious differences are syntax changes. D uses parentheses ( ) instead of angle-brackets < > in a template definition. It also uses the !( ) construct (that is, parentheses preceded by an exclamation point) instead of angle-brackets in template instantiation. Therefore, D V.2 language extends the syntax, when there's a single template argument and no nesting the exclamation point is enough: [edit] Static-ifD provides a template factorial(uint n) { static if (n < 2) const uint factorial = 1; else const uint factorial = n * factorial!(n-1); } [edit] Alias parametersTemplates in D may also accept alias parameters. Alias parameters are similar to C++'s template wrapper(alias Fn) { // Wraps a D function with an "extern(C)" interface. extern(C) void wrapper() { Fn(); } } This sort of template might be useful when interfacing D code with a C API. If a hypothetical C API wants a function pointer, you might use the template like this: void foo() { // ... } some_c_function(&wrapper!(foo)); [edit] Generic programming in EiffelGeneric classes have been a part of Eiffel since the original method and language design. The foundation publications of Eiffel[2][3], use the term genericity to describe the creation and use of generic classes. [edit] Basic genericityGeneric classes are declared with their class name and a list of one or more formal generic parameters. In the following code, class class LIST [G] ... feature -- Access item: G -- The item currently pointed to by cursor ... feature -- Element change put (new_item: G) -- Add `new_item' at the end of the list ... The formal generic parameters are placeholders for arbitrary class names which will be supplied when a declaration of the generic class is made, as shown in the two generic derivations below, where list_of_accounts: LIST [ACCOUNT] -- Account list list_of_deposits: LIST [DEPOSIT] -- Deposit list Within the Eiffel type system, although class [edit] Constrained genericityFor the list class shown above, an actual generic parameter substituting for class SORTED_LIST [G -> COMPARABLE] [edit] Generics in JavaMain article: Generics in Java Support for the generics, or "containers-of-type-T", subset of generic programming were added to the Java programming language in 2004 as part of J2SE 5.0. In Java, generics are checked at compile time for type correctness. The generic type information is then removed via a process called type erasure, and is unavailable at runtime. For example, a [edit] Generic programming in C# and .NETGenerics in C# (and other .NET languages) were added as part of .NET Framework 2.0 in November 2005. Although similar to generics in Java, .NET generics do not apply type erasure, but implement generics as a first class object in the runtime using reification. This design choice provides additional functionality, such as allowing reflection with preservation of generic types, as well as alleviating some of the limitations of erasure (such as being unable to create generic arrays).[4][5] This also means that there is no performance hit from runtime casts and normally expensive boxing conversions. When primitive and value types are used as generic arguments, they get specialized implementations, allowing for efficient generic collections and methods. As in C++ and Java, nested generic types such as Dictionary<string, List<int>> are valid types, however are advised against for member signatures in code analysis design rules[6]. C# (and .NET in general) allows six varieties of generic type constraints using the class Sample { public static void Main() { int[] list = { 0, 1, 2, 3 }; MakeAtLeast<int>(list, 2); // change array to { 2, 2, 2, 3 } } public static void MakeAtLeast<T>(IList<T> list, T lowest) where T : IComparable<T> { for (int i = 0; i < list.Count; i++) if (list[i].CompareTo(lowest) < 0) list[i] = lowest; } } Since all arrays implement the The above method could also be written without generic types, simply using the non-generic [edit] Generic programming in DelphiDelphi supports generics since Delphi 2006, but that was only for the .NET version. In October 2008 generics were added to the win32 version. [edit] Generic programming in Functional languages[edit] Generic programming in HaskellSix of the predefined type classes in Haskell (including data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show) This results in an equality function ( The support for derived instances of [edit] PolyPPolyP was the first generic programming language extension to Haskell. In PolyP, generic functions are called polytypic. The language introduces a special construct in which such polytypic functions can be defined via structural induction over the structure of the pattern functor of a regular datatype. Regular datatypes in PolyP are a subset of Haskell datatypes. A regular datatype t must be of kind * → *, and if a is the formal type argument in the definition, then all recursive calls to t must have the form t a. These restrictions rule out higher-kinded datatypes as well as nested datatypes, where the recursive calls are of a different form. The flatten function in PolyP is here provided as an example: flatten :: Regular d => d a -> [a] flatten = cata fl polytypic fl :: f a [a] -> [a] case f of g+h -> either fl fl g*h -> \(x,y) -> fl x ++ fl y () -> \x -> [] Par -> \x -> [x] Rec -> \x -> x d@g -> concat . flatten . pmap fl Con t -> \x -> [] cata :: Regular d => (FunctorOf d a b -> b) -> d a -> b [edit] Generic HaskellGeneric Haskell is another extension to Haskell, developed at Utrecht University in The Netherlands. The extensions it provides are:
The resulting type-indexed value can be specialised to any type.
As an example, the equality function in Generic Haskell:[8] type Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool type Eq {[ k -> l ]} t1 t2 = forall u1 u2. Eq {[ k ]} u1 u2 -> Eq {[ l ]} (t1 u1) (t2 u2) eq {| t :: k |} :: Eq {[ k ]} t t eq {| Unit |} _ _ = True eq {| :+: |} eqA eqB (Inl a1) (Inl a2) = eqA a1 a2 eq {| :+: |} eqA eqB (Inr b1) (Inr b2) = eqB b1 b2 eq {| :+: |} eqA eqB _ _ = False eq {| :*: |} eqA eqB (a1 :*: b1) (a2 :*: b2) = eqA a1 a2 && eqB b1 b2 eq {| Int |} = (==) eq {| Char |} = (==) eq {| Bool |} = (==) [edit] The "Scrap your boilerplate" approachThe Scrap your boilerplate approach is a lightweight generic programming approach for Haskell (Lämmel and Peyton Jones, 2003). A web site for this approach explains which components of it are currently implemented in GHC. Uniplate is an even simpler package with a similar basic approach.[1] [edit] CleanClean offers generic programming based PolyP and the generic Haskell as supported by the GHC>=6.0. It parametrizes by kind as those but offers overloading. [edit] Generic programming features in other languagesStandard ML and OCaml provide functors, which are similar to class templates and to Ada's generic packages. Scheme syntactic abstractions also have connection to generic programming – these are in fact a superset of templating à la C++. [edit] See also[edit] External links and references[edit] General
[edit] C++/D
[edit] C#/.NET
[edit] Delphi
[edit] Eiffel[edit] Haskell/functional
[edit] Java
[edit] Object Pascal
[edit] Notes
|
| ↑ top of page ↑ | about thumbshots |