Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Generic programming
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==Programming language support for genericity== Genericity facilities have existed in high-level languages since at least the 1970s in languages such as [[ML (programming language)|ML]], [[CLU (programming language)|CLU]] and [[Ada (programming language)|Ada]], and were subsequently adopted by many [[object-based]] and [[object-oriented]] languages, including [[BETA (programming language)|BETA]], [[C++]], [[D (programming language)|D]], [[Eiffel (programming language)|Eiffel]], [[Java (programming language)|Java]], and [[Digital Equipment Corporation|DEC]]'s now defunct [[Trellis-Owl]]. Genericity is implemented and supported differently in various programming languages; the term "generic" has also been used differently in various programming contexts. For example, in [[Forth (programming language)|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 ''genericity'' capacities that, however, are not referred to as such in most Forth texts. Similarly, dynamically typed languages, especially interpreted ones, usually offer ''genericity'' by default as both passing values to functions and value assignment are type-indifferent and such behavior is often used for abstraction or code terseness, however this is not typically labeled ''genericity'' as it's a direct consequence of the dynamic typing system employed by the language.{{citation needed|date=August 2015}} 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. These uses still serve a similar purpose of code-saving and rendering an abstraction. [[Array data type|Arrays]] and [[struct (C programming language)|structs]] can be viewed as predefined generic types. Every usage of an array or struct type instantiates a new concrete type, or reuses a previous instantiated type. Array element types and struct element types are parameterized types, which are used to instantiate the corresponding generic type. All this is usually built-in in the [[compiler]] and the syntax differs from other generic constructs. Some [[extensible programming|extensible programming languages]] try to unify built-in and user defined generic types. A broad survey of genericity mechanisms in programming languages follows. For a specific survey comparing suitability of mechanisms for generic programming, see.<ref>{{Cite CiteSeerX |title=An extended comparative study of language support for generic programming (preprint) |author1=R. Garcia |author2=J. Ja Μrvi |author3=A. Lumsdaine |author4=J. Siek |author5=J. Willcock |year=2005 |citeseerx=10.1.1.110.122}}</ref> ===In object-oriented languages=== When creating container classes in statically typed languages, it is inconvenient to write specific implementations for each datatype contained, especially if the code for each datatype is virtually identical. For example, in C++, this duplication of code can be circumvented by defining a class template: <syntaxhighlight lang="Cpp"> template<typename T> class List { // Class contents. }; List<Animal> list_of_animals; List<Car> list_of_cars; </syntaxhighlight> Above, <code>T</code> is a placeholder for whatever type is specified when the list is created. These "containers-of-type-T", commonly called [[template (programming)|templates]], allow a class to be reused with different datatypes as long as certain contracts such as [[Subtyping|subtype]]s and [[Type signature|signature]] are kept. This genericity mechanism should not be confused with ''[[polymorphism (computer science)|inclusion polymorphism]]'', which is the [[algorithm]]ic usage of exchangeable sub-classes: for instance, a list of objects of type <code>Moving_Object</code> containing objects of type <code>Animal</code> and <code>Car</code>. Templates can also be used for type-independent functions as in the <code>Swap</code> example below: <syntaxhighlight lang="Cpp"> // "&" denotes a reference template<typename T> void Swap(T& a, T& b) { // A similar, but safer and potentially faster function // is defined in the standard library header <utility> T temp = b; b = a; a = temp; } std::string world = "World!"; std::string hello = "Hello, "; Swap(world, hello); std::cout << world << hello << β\nβ; // Output is "Hello, World!". </syntaxhighlight> The C++ <code>template</code> construct used above is widely cited{{Citation needed|date=May 2010}} as the genericity construct that popularized the notion among programmers and [[List of programming language researchers|language designers]] and supports many generic programming idioms. The D language also offers fully generic-capable templates based on the C++ precedent but with a simplified syntax. The Java language has provided genericity facilities syntactically based on C++'s since the introduction of [[Java Platform, Standard Edition]] (J2SE) 5.0. [[C Sharp (programming language)|C#]] 2.0, [[Oxygene (programming language)|Oxygene]] 1.5 (formerly Chrome) and [[Visual Basic (.NET)]] 2005 have constructs that exploit the support for generics present in Microsoft [[.NET Framework]] since version 2.0. ====Generics in Ada==== {{unreferenced|section|date=January 2024}} [[Ada (programming language)|Ada]] 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''.<ref>{{Cite web |title=Generic Units |url=https://www.adaic.org/resources/add_content/standards/05rm/html/RM-12.html |access-date=2024-04-25 |website=www.adaic.org}}</ref> 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 (program lifecycle phase)|run-time]], for example inside a loop. =====Example===== The specification of a generic package: <syntaxhighlight lang="ada"> 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; </syntaxhighlight> Instantiating the generic package: <syntaxhighlight lang="ADA"> 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 </syntaxhighlight> Using an instance of a generic package: <syntaxhighlight lang="ada"> 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; </syntaxhighlight> =====Advantages and limits===== The 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: <syntaxhighlight lang="ada"> 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; </syntaxhighlight> 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: * the compiler can implement ''shared generics'': the object code for a generic unit can be shared between all instances (unless the programmer requests inlining of subprograms, of course). As further consequences: ** there is no possibility of code bloat (code bloat is common in C++ and requires special care, as explained below). ** it is possible to instantiate generics at run-time, and at compile time, since no new object code is required for a new instance. ** actual objects corresponding to a generic formal object are always considered to be non-static inside the generic; see [[wikibooks:Ada Programming/Generics#Generic formal objects|Generic formal objects]] in the Wikibook for details and consequences. * all instances of a generic being exactly the same, it is easier to review and understand programs written by others; there are no "special cases" to take into account. * all instantiations being explicit, there are no hidden instantiations that might make it difficult to understand the program. * Ada does not permit arbitrary computation at compile time, because operations on generic arguments are performed at runtime. ====Templates in C++==== {{Main|Template (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 (program lifecycle phase)|run-time]]. Using template specialization, C++ Templates are [[Turing complete]]. =====Technical overview===== There are many kinds of templates, the most common being 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 <code>max(x, y)</code> that creates functions that return either ''x'' or ''y,'' whichever is larger. <code>max()</code> could be defined like this: <syntaxhighlight lang="cpp"> template<typename T> T max(T x, T y) { return x < y ? y : x; } </syntaxhighlight> ''Specializations'' of this function template, instantiations with specific types, can be called just like an ordinary function: <syntaxhighlight lang="cpp"> std::cout << max(3, 7); // Outputs 7. </syntaxhighlight> The compiler examines the arguments used to call <code>max</code> and determines that this is a call to <code>max(int, int)</code>. It then instantiates a version of the function where the parameterizing type <code>T</code> is <code>int</code>, making the equivalent of the following function: <syntaxhighlight lang="cpp"> int max(int x, int y) { return x < y ? y : x; } </syntaxhighlight> This works whether the arguments <code>x</code> and <code>y</code> are integers, strings, or any other type for which the expression <code>x < y</code> is sensible, or more specifically, for any type for which <code>operator<</code> is defined. Common inheritance is not needed for the set of types that can be used, and so it is very similar to [[Duck typing#Templates or generic types|duck typing]]. A program defining a custom data type can use [[operator overloading]] to define the meaning of <code><</code> for that type, thus allowing its use with the <code>max()</code> function template. While this may seem a minor benefit in this isolated example, in the context of a comprehensive library like the STL it allows the programmer to get extensive functionality for a new data type, just by defining a few operators for it. Merely defining <code><</code> allows a type to be used with the standard <code>sort()</code>, <code>stable_sort()</code>, and <code>binary_search()</code> algorithms or to be put inside data structures such as <code>set</code>s, [[heap (programming)|heap]]s, and [[associative array]]s. C++ templates are completely [[type safe]] at compile time. As a demonstration, the standard type <code>complex</code> does not define the <code><</code> operator, because there is no strict order on [[complex number]]s. Therefore, <code>max(x, y)</code> will fail with a compile error, if ''x'' and ''y'' are <code>complex</code> values. Likewise, other templates that rely on <code><</code> cannot be applied to <code>complex</code> data unless a comparison (in the form of a functor or function) is provided. E.g.: A <code>complex</code> cannot be used as key for a <code>map</code> unless a comparison is provided. Unfortunately, compilers historically generate somewhat esoteric, long, and unhelpful error messages for this sort of error. Ensuring that a certain object adheres to a [[protocol (computer science)|method protocol]] can alleviate this issue. Languages which use <code>compare</code> instead of <code><</code> can also use <code>complex</code> values as keys. Another 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 <code>list<int></code>. A list of strings is denoted <code>list<string></code>. A <code>list</code> has a set of standard functions associated with it, that work for any compatible parameterizing types. =====Template specialization===== A 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 <code>sort()</code> template function. One of the primary activities that such a function does is to swap or exchange the values in two of the container's positions. If the values are large (in terms of the number of bytes it takes to store each of them), then it is often quicker to first build a separate list of pointers to the objects, sort those pointers, and then build the final sorted sequence. If the values are quite small however it is usually fastest to just swap the values in-place as needed. Furthermore, if the parameterized type is already of some pointer-type, then there is no need to build a separate pointer array. Template specialization allows the template creator to write different implementations and to specify the characteristics that the parameterized type(s) must have for each implementation to be used. Unlike function templates, class templates can be [[Partial template specialization|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. =====Advantages and disadvantages===== Some uses of templates, such as the <code>max()</code> function, were formerly filled by function-like [[preprocessor]] [[Macro (computer science)|macros]] (a legacy of the [[C (programming language)|C]] language). For example, here is a possible implementation of such macro: <syntaxhighlight lang="cpp"> #define max(a,b) ((a) < (b) ? (b) : (a)) </syntaxhighlight> Macros are expanded (copy pasted) by the [[preprocessor]], before program compiling; templates are actual real functions. Macros are always expanded inline; templates can also be [[inline function]]s when the compiler deems it appropriate. 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, such as evaluating parameters with side effects twice. Perhaps most importantly, templates were designed to be applicable to much larger problems than macros. There are four primary drawbacks to the use of templates: supported features, compiler support, poor error messages (usually with pre C++20 ''substitution failure is not an error'' ([[SFINAE]])), and [[code bloat]]: # Templates in C++ lack many features, which makes implementing them and using them in a straightforward way often impossible. Instead programmers have to rely on complex tricks which leads to bloated, hard to understand and hard to maintain code. Current developments in the C++ standards exacerbate this problem by making heavy use of these tricks and building a lot of new features for templates on them or with them intended. # Many compilers historically had poor support for templates, thus the use of templates could make code somewhat less portable. Support may also be poor when a C++ compiler is being used with a [[Linker (computing)|linker]] that is not C++-aware, or when attempting to use templates across [[Library (computer science)#Shared libraries|shared library]] boundaries. # Compilers can produce confusing, long, and sometimes unhelpful error messages when errors are detected in code that uses SFINAE.<ref>[https://www.stroustrup.com/N1522-concept-criteria.pdf Stroustrup, Dos Reis (2003): Concepts - Design choices for template argument checking]</ref> This can make templates difficult to develop with. # Finally, the use of templates requires the compiler to generate a separate ''instance'' of the templated class or function for every 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. However, judicious use of template specialization and derivation can dramatically reduce such code bloat in some cases:{{Blockquote|So, can derivation be used to reduce the problem of code replicated because templates are used? This would involve deriving a template from an ordinary class. This technique proved successful in curbing code bloat in real use. People who do not use a technique like this have found that replicated code can cost megabytes of code space even in moderate size programs.|[[Bjarne Stroustrup]]|The Design and Evolution of C++, 1994<ref name="Stroustrup94Design">{{cite book |last1=Stroustrup |first1=Bjarne |author1-link=Bjarne Stroustrup |year=1994 |title=The Design and Evolution of C++ |publisher=Addison-Wesley |location=Reading, Massachusetts |isbn=978-81-317-1608-3 |pages=346β348 |chapter=15.5 Avoiding Code Replication |bibcode=1994dec..book.....S}}</ref>}} # Templated classes or functions may require an ''explicit specialization'' of the template class which would require rewriting of an entire class for a specific template parameters used by it. The extra instantiations generated by templates can also cause some 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, the implementation source code for the template must be completely available (e.g. included in a header) to the translation unit (source file) using it. Templates, including much of the Standard Library, if not included in header files, 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|date=March 2009}} ====Templates in D==== The [[D (programming language)|D]] language supports templates based in design on C++. Most C++ template idioms work in D without alteration, but D adds some functionality: * Template parameters in D are not restricted to just types and primitive values (as it was in C++ before C++20), but also allow arbitrary compile-time values (such as strings and struct literals), and aliases to arbitrary identifiers, including other templates or template instantiations. * Template constraints and the <code>static if</code> statement provide an alternative to respectively C++'s [[C++ concepts]] and <code>if constexpr</code>. * The <code>is(...)</code> expression allows speculative instantiation to verify an object's traits at compile time. * The <code>auto</code> keyword and the <code>[[typeof]]</code> expression allow [[type inference]] for variable declarations and function return values, which in turn allows "Voldemort types" (types that do not have a global name).<ref>{{cite web |last=Bright |first=Walter |title=Voldemort Types in D |url =https://www.drdobbs.com/cpp/voldemort-types-in-d/232901591 |website=Dr. Dobbs |access-date=3 June 2015}}</ref> Templates in D use a different syntax than in C++: whereas in C++ template parameters are wrapped in angular brackets (<code>Template<param1, param2></code>), D uses an exclamation sign and parentheses: <code>Template!(param1, param2)</code>. This avoids the [[Template (C++)#Advantages and disadvantages|C++ parsing difficulties]] due to ambiguity with comparison operators. If there is only one parameter, the parentheses can be omitted. Conventionally, D combines the above features to provide [[compile-time polymorphism]] using trait-based generic programming. For example, an input [[Range (computer programming)#Range as an alternative to iterator|range]] is defined as any type that satisfies the checks performed by <code>isInputRange</code>, which is defined as follows: <syntaxhighlight lang="d"> template isInputRange(R) { enum bool isInputRange = is(typeof( (inout int = 0) { R r = R.init; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range })); } </syntaxhighlight> A function that accepts only input ranges can then use the above template in a template constraint: <syntaxhighlight lang="d"> auto fun(Range)(Range range) if (isInputRange!Range) { // ... } </syntaxhighlight> =====Code generation===== In addition to template metaprogramming, D also provides several features to enable compile-time code generation: * The <code>import</code> expression allows reading a file from disk and using its contents as a string expression. * Compile-time reflection allows enumerating and inspecting declarations and their members during compiling. * User-defined [[Attribute (computing)|attributes]] allow users to attach arbitrary identifiers to declarations, which can then be enumerated using compile-time reflection. * [[Compile-time function execution]] (CTFE) allows a subset of D (restricted to safe operations) to be interpreted during compiling. * String mixins allow evaluating and compiling the contents of a string expression as D code that becomes part of the program. Combining the above allows generating code based on existing declarations. For example, D serialization frameworks can enumerate a type's members and generate specialized functions for each serialized type to perform serialization and deserialization. User-defined attributes could further indicate serialization rules. The <code>import</code> expression and compile-time function execution also allow efficiently implementing [[domain-specific language]]s. For example, given a function that takes a string containing an HTML template and returns equivalent D source code, it is possible to use it in the following way: <syntaxhighlight lang="d"> // Import the contents of example.htt as a string manifest constant. enum htmlTemplate = import("example.htt"); // Transpile the HTML template to D code. enum htmlDCode = htmlTemplateToD(htmlTemplate); // Paste the contents of htmlDCode as D code. mixin(htmlDCode); </syntaxhighlight> ====Genericity in Eiffel==== Generic classes have been a part of [[Eiffel (programming language)|Eiffel]] since the original method and language design. The foundation publications of Eiffel,<ref>''Object-Oriented Software Construction,'' Prentice Hall, 1988, and ''Object-Oriented Software Construction, second edition,'' Prentice Hall, 1997.</ref><ref>''Eiffel: The Language,'' Prentice Hall, 1991.</ref> use the term ''genericity'' to describe creating and using generic classes. =====Basic, unconstrained genericity===== Generic classes are declared with their class name and a list of one or more ''formal generic parameters''. In the following code, class <code lang=Eiffel>LIST</code> has one formal generic parameter <code lang=Eiffel>G</code> <syntaxhighlight lang="eiffel"> 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 ... </syntaxhighlight> The formal generic parameters are placeholders for arbitrary class names that will be supplied when a declaration of the generic class is made, as shown in the two ''generic derivations'' below, where <code lang=Eiffel>ACCOUNT</code> and <code lang=Eiffel>DEPOSIT</code> are other class names. <code lang=Eiffel>ACCOUNT</code> and <code lang=Eiffel>DEPOSIT</code> are considered ''actual generic parameters'' as they provide real class names to substitute for <code lang=Eiffel>G</code> in actual use. <syntaxhighlight lang="eiffel"> list_of_accounts: LIST [ACCOUNT] -- Account list list_of_deposits: LIST [DEPOSIT] -- Deposit list </syntaxhighlight> Within the Eiffel type system, although class <code lang=Eiffel>LIST [G]</code> is considered a class, it is not considered a type. However, a generic derivation of <code lang=Eiffel>LIST [G]</code> such as <code lang=Eiffel>LIST [ACCOUNT]</code> is considered a type. =====Constrained genericity===== For the list class shown above, an actual generic parameter substituting for <code lang=Eiffel>G</code> can be any other available class. To constrain the set of classes from which valid actual generic parameters can be chosen, a ''generic constraint'' can be specified. In the declaration of class <code lang=Eiffel>SORTED_LIST</code> below, the generic constraint dictates that any valid actual generic parameter will be a class that inherits from class <code lang=Eiffel>COMPARABLE</code>. The generic constraint ensures that elements of a <code lang=Eiffel>SORTED_LIST</code> can in fact be sorted. <syntaxhighlight lang="eiffel"> class SORTED_LIST [G -> COMPARABLE] </syntaxhighlight> ====Generics in Java====<!-- This section is linked from [[Comparison of Java and C++]] --> {{Main|Generics in Java}} Support for the ''generics'', or "containers-of-type-T" was added to the [[Java programming language]] in 2004 as part of J2SE 5.0. In Java, generics are only checked at compile time for type correctness. The generic type information is then removed via a process called [[type erasure]], to maintain compatibility with old [[JVM]] implementations, making it unavailable at runtime.{{sfn|Bloch|2018|loc=Β§Item 28: Prefer lists to arrays|p=126}} For example, a <code>List<String></code> is converted to the raw type <code>List</code>. The compiler inserts [[Type conversion|type casts]] to convert the elements to the <code>String</code> type when they are retrieved from the list, reducing performance compared to other implementations such as C++ templates. ====Genericity in .NET [C#, VB.NET]==== Generics were added as part of [[.NET Framework#.NET Framework 2.0|.NET Framework 2.0]] in November 2005, based on a research prototype from Microsoft Research started in 1999.<ref>[https://docs.microsoft.com/en-us/archive/blogs/dsyme/netc-generics-history-some-photos-from-feb-1999 .NET/C# Generics History: Some Photos From Feb 1999]</ref> Although similar to generics in Java, .NET generics do not apply [[type erasure]],{{sfn|Albahari|2022}}{{rp|208β209}} but implement generics as a first class mechanism in the runtime using [[Reification (computer science)|reification]]. This design choice provides additional functionality, such as allowing [[Reflective programming|reflection]] with preservation of generic types, and alleviating some of the limits of erasure (such as being unable to create generic arrays).<ref>[https://www.ondotnet.com/pub/a/dotnet/2005/10/17/interview-with-anders-hejlsberg.html C#: Yesterday, Today, and Tomorrow: An Interview with Anders Hejlsberg]</ref><ref>[https://www.artima.com/intv/generics2.html Generics in C#, Java, and C++]</ref> This also means that there is no performance hit from runtime [[Type conversion|casts]] and normally expensive [[Boxing (computer science)|boxing conversions]]. When primitive and value types are used as generic arguments, they get specialized implementations, allowing for efficient generic [[Collection class|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.<ref>[https://msdn.microsoft.com/en-us/library/ms182144.aspx Code Analysis CA1006: Do not nest generic types in member signatures]</ref> .NET allows six varieties of generic type constraints using the <code>where</code> keyword including restricting generic types to be value types, to be classes, to have constructors, and to implement interfaces.<ref>[https://msdn2.microsoft.com/en-us/library/d5x73970.aspx Constraints on Type Parameters (C# Programming Guide)]</ref> Below is an example with an interface constraint: <syntaxhighlight lang="csharp" line="1"> using System; class Sample { static void Main() { int[] array = { 0, 1, 2, 3 }; MakeAtLeast<int>(array, 2); // Change array to { 2, 2, 2, 3 } foreach (int i in array) Console.WriteLine(i); // Print results. Console.ReadKey(true); } static void MakeAtLeast<T>(T[] list, T lowest) where T : IComparable<T> { for (int i = 0; i < list.Length; i++) if (list[i].CompareTo(lowest) < 0) list[i] = lowest; } } </syntaxhighlight> The <code>MakeAtLeast()</code> method allows operation on arrays, with elements of generic type <code>T</code>. The method's type constraint indicates that the method is applicable to any type <code>T</code> that implements the generic <code>IComparable<T></code> interface. This ensures a [[compile time]] error, if the method is called if the type does not support comparison. The interface provides the generic method <code>CompareTo(T)</code>. The above method could also be written without generic types, simply using the non-generic <code>Array</code> type. However, since arrays are [[Covariance and contravariance (computer science)|contravariant]], the casting would not be [[type safe]], and the compiler would be unable to find certain possible errors that would otherwise be caught when using generic types. In addition, the method would need to access the array items as <code>object</code>s instead, and would require [[Type conversion|casting]] to compare two elements. (For value types like types such as <code>int</code> this requires a [[Boxing (computer science)|boxing]] conversion, although this can be worked around using the <code>Comparer<T></code> class, as is done in the standard collection classes.) A notable behavior of static members in a generic .NET class is static member instantiation per run-time type (see example below). <syntaxhighlight lang="csharp"> // A generic class public class GenTest<T> { // A static variable - will be created for each type on reflection static CountedInstances OnePerType = new CountedInstances(); // a data member private T _t; // simple constructor public GenTest(T t) { _t = t; } } // a class public class CountedInstances { //Static variable - this will be incremented once per instance public static int Counter; //simple constructor public CountedInstances() { //increase counter by one during object instantiation CountedInstances.Counter++; } } // main code entry point // at the end of execution, CountedInstances.Counter = 2 GenTest<int> g1 = new GenTest<int>(1); GenTest<int> g11 = new GenTest<int>(11); GenTest<int> g111 = new GenTest<int>(111); GenTest<double> g2 = new GenTest<double>(1.0); </syntaxhighlight> ====Genericity in Pascal==== For [[Pascal (programming language)|Pascal]], generics were first implemented in 2006, in the implementation [[#In Free Pascal|Free Pascal]]. =====In Delphi===== The [[Object Pascal]] dialect [[Delphi (software)|Delphi]] acquired generics in the 2007 Delphi 11 release by [[CodeGear]], initially only with the .NET compiler (since discontinued) before being added to the native code in the 2009 Delphi 12 release. The semantics and abilities of Delphi generics are largely modelled on those of generics in .NET 2.0, though the implementation is by necessity quite different. Here is a more or less direct translation of the first C# example shown above: <syntaxhighlight lang="delphi"> program Sample; {$APPTYPE CONSOLE} uses Generics.Defaults; //for IComparer<> type TUtils = class class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T; Comparer: IComparer<T>); overload; class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T); overload; end; class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T; Comparer: IComparer<T>); var I: Integer; begin if Comparer = nil then Comparer := TComparer<T>.Default; for I := Low(Arr) to High(Arr) do if Comparer.Compare(Arr[I], Lowest) < 0 then Arr[I] := Lowest; end; class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T); begin MakeAtLeast<T>(Arr, Lowest, nil); end; var Ints: TArray<Integer>; Value: Integer; begin Ints := TArray<Integer>.Create(0, 1, 2, 3); TUtils.MakeAtLeast<Integer>(Ints, 2); for Value in Ints do WriteLn(Value); ReadLn; end. </syntaxhighlight> As with C#, methods and whole types can have one or more type parameters. In the example, TArray is a generic type (defined by the language) and MakeAtLeast a generic method. The available constraints are very similar to the available constraints in C#: any value type, any class, a specific class or interface, and a class with a parameterless constructor. Multiple constraints act as an additive union. =====In Free Pascal===== [[Free Pascal]] implemented generics in 2006 in [[Free Pascal#Version 2.2.x|version 2.2.0]], before Delphi and with different syntax and semantics. However, since FPC version 2.6.0, the Delphi-style syntax is available when using the language mode <code>{$mode Delphi}</code>. Thus, Free Pascal code supports generics in either style. Delphi and Free Pascal example: <syntaxhighlight lang="delphi"> // Delphi style unit A; {$ifdef fpc} {$mode delphi} {$endif} interface type TGenericClass<T> = class function Foo(const AValue: T): T; end; implementation function TGenericClass<T>.Foo(const AValue: T): T; begin Result := AValue + AValue; end; end. // Free Pascal's ObjFPC style unit B; {$ifdef fpc} {$mode objfpc} {$endif} interface type generic TGenericClass<T> = class function Foo(const AValue: T): T; end; implementation function TGenericClass.Foo(const AValue: T): T; begin Result := AValue + AValue; end; end. // example usage, Delphi style program TestGenDelphi; {$ifdef fpc} {$mode delphi} {$endif} uses A,B; var GC1: A.TGenericClass<Integer>; GC2: B.TGenericClass<String>; begin GC1 := A.TGenericClass<Integer>.Create; GC2 := B.TGenericClass<String>.Create; WriteLn(GC1.Foo(100)); // 200 WriteLn(GC2.Foo('hello')); // hellohello GC1.Free; GC2.Free; end. // example usage, ObjFPC style program TestGenDelphi; {$ifdef fpc} {$mode objfpc} {$endif} uses A,B; // required in ObjFPC type TAGenericClassInt = specialize A.TGenericClass<Integer>; TBGenericClassString = specialize B.TGenericClass<String>; var GC1: TAGenericClassInt; GC2: TBGenericClassString; begin GC1 := TAGenericClassInt.Create; GC2 := TBGenericClassString.Create; WriteLn(GC1.Foo(100)); // 200 WriteLn(GC2.Foo('hello')); // hellohello GC1.Free; GC2.Free; end. </syntaxhighlight> ===Functional languages=== ====Genericity in Haskell==== The [[type class]] mechanism of [[Haskell]] supports generic programming. Six of the predefined type classes in Haskell (including <code>Eq</code>, the types that can be compared for equality, and <code>Show</code>, the types whose values can be rendered as strings) have the special property of supporting ''derived instances.'' This means that a programmer defining a new type can state that this type is to be an instance of one of these special type classes, without providing implementations of the class methods as is usually necessary when declaring class instances. All the necessary methods will be "derived" β that is, constructed automatically β based on the structure of the type. For example, the following declaration of a type of [[binary tree]]s states that it is to be an instance of the classes <code>Eq</code> and <code>Show</code>: <syntaxhighlight lang="haskell"> data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show) </syntaxhighlight> This results in an equality function (<code>==</code>) and a string representation function (<code>show</code>) being automatically defined for any type of the form <code>BinTree T</code> provided that <code>T</code> itself supports those operations. The support for derived instances of <code>Eq</code> and <code>Show</code> makes their methods <code>==</code> and <code>show</code> generic in a qualitatively different way from parametrically polymorphic functions: these "functions" (more accurately, type-indexed families of functions) can be applied to values of various types, and although they behave differently for every argument type, little work is needed to add support for a new type. Ralf Hinze (2004) has shown that a similar effect can be achieved for user-defined type classes by certain programming techniques. Other researchers have proposed approaches to this and other kinds of genericity in the context of Haskell and extensions to Haskell (discussed below). ===== PolyP ===== PolyP 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 (type theory)|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 and nested datatypes, where the recursive calls are of a different form. The flatten function in PolyP is here provided as an example: <syntaxhighlight lang="haskell"> 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 </syntaxhighlight> =====Generic Haskell===== Generic Haskell is another extension to [[Haskell]], developed at [[Utrecht University]] in the [[Netherlands]]. The extensions it provides are: *''Type-indexed values'' are defined as a value indexed over the various Haskell type constructors (unit, primitive types, sums, products, and user-defined type constructors). In addition, we can also specify the behaviour of a type-indexed values for a specific constructor using ''constructor cases'', and reuse one generic definition in another using ''default cases''. The resulting type-indexed value can be specialized to any type. *''Kind-indexed types'' are types indexed over kinds, defined by giving a case for both ''*'' and ''k β k'''. Instances are obtained by applying the kind-indexed type to a kind. *Generic definitions can be used by applying them to a type or kind. This is called ''generic application''. The result is a type or value, depending on which sort of generic definition is applied. *''Generic abstraction'' enables generic definitions be defined by abstracting a type parameter (of a given kind). *''Type-indexed types'' are types that are indexed over the type constructors. These can be used to give types to more involved generic values. The resulting type-indexed types can be specialized to any type. As an example, the equality function in Generic Haskell:<ref>[https://www.cs.uu.nl/research/projects/generic-haskell/compiler/diamond/GHUsersGuide.pdf The Generic Haskell User's Guide]</ref> <syntaxhighlight lang="haskell"> 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 |} = (==) </syntaxhighlight> ====Clean==== [[Clean (programming language)|Clean]] offers generic programming based [[#PolyP|PolyP]] and the [[#Generic Haskell|Generic Haskell]] as supported by the GHC β₯ 6.0. It parametrizes by kind as those but offers overloading. ===Other languages=== Languages in the [[ML (programming language)|ML]] family support generic programming through [[parametric polymorphism]] and generic [[Modular programming|modules]] called ''functors.'' Both [[Standard ML]] and [[OCaml]] provide functors, which are similar to class templates and to Ada's generic packages. [[Scheme (programming language)|Scheme]] syntactic abstractions also have a connection to genericity β these are in fact a superset of C++ templates. A [[Verilog]] module may take one or more parameters, to which their actual values are assigned upon the instantiation of the module. One example is a generic [[Hardware register|register]] array where the array width is given via a parameter. Such an array, combined with a generic wire vector, can make a generic buffer or memory module with an arbitrary bit width out of a single module implementation.<ref>Verilog by Example, Section ''The Rest for Reference''. Blaine C. Readler, Full Arc Press, 2011. {{ISBN|978-0-9834973-0-1}}</ref> [[VHDL]], being derived from Ada, also has generic abilities.<ref>https://www.ics.uci.edu/~jmoorkan/vhdlref/generics.html VHDL Reference</ref> [[C (programming language)|C]] supports "type-generic expressions" using the {{c-lang|_Generic}} keyword:<ref name="N1516">[https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1516.pdf WG14 N1516 Committee Draft β October 4, 2010]</ref> <syntaxhighlight lang="c"> #define cbrt(x) _Generic((x), long double: cbrtl, \ default: cbrt, \ float: cbrtf)(x)</syntaxhighlight>
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)