通用性
至少在20世紀70年代,通用設施已經存在於ML,CLU和Ada等語言中,並且隨後被許多基於對象和面向對象的語言所採用,包括BETA,C ++,D,Eiffel,Java,和DEC已經不復存在的Trellis-Owl語言。
在各種程式語言中以不同方式實現和支持通用性;術語“通用”在各種編程環境中也被不同地使用。例如,在Forth中,編譯器可以在編譯時執行代碼,並且可以動態地為這些單詞創建新的編譯器關鍵字和新實現。它幾乎沒有暴露編譯器行為的單詞,因此自然地提供了通用性能力,然而,在大多數Forth文本中並未提及這些能力。類似地,動態類型語言,特別是解釋型語言,默認情況下通常提供通用性,因為向函式和值賦值傳遞值都是類型無關緊要的,並且這種行為通常用於抽象或代碼簡潔,但是這通常不是標記為通用性,因為它是語言所採用的動態類型系統的直接後果。該術語已用於函式式編程,特別是在類似Haskell的語言中,它使用結構類型系統,其中類型總是參數化的,並且這些類型上的實際代碼是通用的。這些用法仍然用於代碼保存和抽象呈現的類似目的。
可以將數組和結構視為預定義的泛型類型。數組或結構類型的每次使用都會實例化一個新的具體類型,或者重用先前實例化的類型。數組元素類型和結構元素類型是參數化類型,用於實例化相應的泛型類型。所有這些通常都內置在編譯器中,語法與其他通用結構不同。一些可擴展的程式語言嘗試統一內置和用戶定義的泛型類型。
面向對象的代碼
在使用靜態類型語言創建容器類時,為每個包含的數據類型編寫特定實現是不方便的,特別是如果每個數據類型的代碼實際上是相同的。 例如,在C ++中,可以通過定義類模板來規避這種代碼重複:
template<typename T>class List{ /* class contents */};List<Animal> list_of_animals;List<Car> list_of_cars;
在上面,T是占位符,用於創建列表時指定的任何類型。 這些“tank-of-type-T”(通常稱為模板)允許使用不同的數據類型重用類,只要保留某些契約(如子類型和簽名)即可。 這種通用性機制不應與包含多態性相混淆,包含多態性是可交換子類的算法用法:例如,包含類型為Animal和Car的對象的Moving_Object類型的對象列表。 模板也可用於與類型無關的功能,如下面的Swap示例所示:
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!"
上面使用的C ++模板構造被廣泛引用[引用需要]作為通用性構造,在程式設計師和語言設計者之間普及概念並支持許多通用編程習語。 D程式語言還提供了基於C ++先例但具有簡化語法的完全通用的模板。 自J2SE 5.0引入以來,Java程式語言在語法上提供了基於C ++的通用性設施。
C#2.0,Oxygene 1.5(也稱為Chrome)和Visual Basic .NET 2005具有利用自2.0版以來對Microsoft .NET Framework中存在的泛型的支持的構造。
用C ++進行擴展
C ++使用模板來啟用通用編程技術。 C ++標準庫包括標準模板庫或STL,它為常見的數據結構和算法提供模板框架。 C ++中的模板也可用於模板元編程,這是一種在編譯時而不是運行時預先評估某些代碼的方法。 使用模板專業化,C ++模板被認為是圖靈完整的。
D中的模板
D程式語言支持基於C ++設計的模板。大多數C ++模板習語都會在沒有改動的情況下轉移到D,但D增加了一些額外的功能:
D中的模板參數不僅限於類型和原始值,還允許任意編譯時值(如字元串和結構文字),以及任意標識符的別名,包括其他模板或模板實例。
1、模板約束和靜態if語句提供了替代C ++的替換失敗不是錯誤(SFINAE)機制,類似於C ++概念。
2、 is(...)表達式允許推測實例化在編譯時驗證對象的特徵。
3、 auto關鍵字和typeof表達式允許對變數聲明和函式返回值進行類型推斷,這反過來允許“Voldemort類型”(沒有全局名稱的類型)。
D中的模板使用與C ++不同的語法:而在C ++中,模板參數包含在尖括弧中(Template <param1, param2>), D使用驚嘆號和括弧:Template!(param1,param2)。這避免了由於比較運算符的模糊性導致的C ++解析困難。如果只有一個參數,則可以省略括弧。
通常,D結合上述特徵以使用基於特徵的通用編程來提供編譯時多態性。例如,輸入範圍定義為滿足isInputRange執行的檢查的任何類型,其定義如下:
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 }));}
然後,只接受輸入範圍的函式可以在模板約束中使用上述模板:
auto fun(Range)(Range range) if (isInputRange!Range) { // ...}
功能
Haskell中的通用性
Haskell的類型類機制支持泛型編程。 Haskell中的六個預定義類型類(包括Eq,可以比較相等的類型和Show,其值可以呈現為字元串的類型)具有支持派生實例的特殊屬性。 這意味著定義新類型的程式設計師可以聲明此類型是這些特殊類型類之一的實例,而不提供在聲明類實例時通常需要的類方法的實現。 所有必要的方法都將“派生” - 即根據類型的結構自動構造。 例如,以下一類二叉樹的聲明聲明它是Eq和Show類的一個實例:
data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show)
這導致相等函式(==)和字元串表示函式(show)被自動定義為任何類型的表單BinTree T,前提是T本身支持這些操作。
對Eq和Show的派生實例的支持使得它們的方法==並以與參數多態函式在質量上不同的方式顯示泛型:這些“函式”(更準確地說,類型索引的函式族)可以套用於值 各種類型,雖然它們對於每種參數類型的行為都不同,但是需要很少的工作來添加對新類型的支持。 Ralf Hinze(2004)已經表明,通過某些編程技術可以為用戶定義的類型類實現類似的效果。 其他研究人員已經在Haskell和Haskell的擴展(下面討論)的上下文中提出了這種和其他類型的通用性的方法。
PolyP
PolyP是Haskell的第一個通用程式語言擴展。 在PolyP中,泛型函式稱為polytypic。 該語言引入了一種特殊的構造,其中可以通過對常規數據類型的模式函子的結構的結構歸納來定義這種多型函式。 PolyP中的常規數據類型是Haskell數據類型的子集。 常規數據類型t必須是kind *→*,如果a是定義中的形式類型參數,則對t的所有遞歸調用必須具有t a形式。 這些限制排除了較高級別的數據類型以及嵌套數據類型,其中遞歸調用具有不同的形式。 PolyP中的展平功能在此處作為示例提供:
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
通用Haskell
Generic Haskell是荷蘭烏得勒支大學開發的Haskell的另一個擴展。它提供的擴展是:
類型索引值被定義為在各種Haskell類型構造函式(單元,基元類型,總和,產品和用戶定義的類型構造函式)上索引的值。此外,我們還可以使用構造函式案例為特定構造函式指定類型索引值的行為,並使用默認情況在另一個中重用一個泛型定義。
例如,Generic 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 |} = (==)
其他通用代碼
ML系列程式語言通過參數多態和支持仿函式的通用模組支持泛型編程。 標準ML和OCaml都提供了類似於類模板和Ada通用包的仿函式。 Scheme語法抽象也與泛型有關 - 這些實際上是模板化C ++的超集。
Verilog模組可以採用一個或多個參數,在模組實例化時將其實際值分配給該參數。 一個示例是通用暫存器陣列,其中陣列寬度通過參數給出。 這種陣列與通用線矢量相結合,可以使單個模組實現中的任意位寬的通用緩衝器或存儲器模組成為可能。
VHDL源自Ada,也具有通用能力。