-{H|
}-

| 類型系統 |
| --- |
| 
一般概念 |
| * 类型安全
* 强类型与弱类型
 |
| 
主要分类 |
| * 静态类型与动态类型
* 明示类型（英语：）与推论类型
* 名称类型（英语：）与结构类型（英语：）
* 鸭子类型
 |
| 
次要分类 |
| * 抽象类型
* 依赖类型
* 流程敏感类型（英语：）
* 渐进类型（英语：）
* 交集类型（英语：）
* 潜在类型（英语：）
* 细化类型（英语：）
* 子结构类型（英语：）
* 唯一类型（英语：）
* 会话类型（英语：）
 |
| * 
* 论
* 编
 |

在计算机科学中，**类型系統**（英語：）用于定義如何將程式語言中的數值和運算式归類为许多不同的**型別**，如何操作这些型別，这些型別如何互相作用。型別可以确认一个值或者一组值具有特定的意义和目的（雖然某些型別，如抽象型別和函式型別，在程式執行中，可能不表示為值）。型別系統在各種語言之間有非常大的不同，也許，最主要的差異存在於編譯時期的語法，以及執行時期的操作实现方式。

編譯器可能使用值的靜態型別以最佳化所需的儲存區，並選取對值運算時的較佳演算法。例如，在許多C編譯器中，「浮點數」資料型別是以32 位元表示，與IEEE 754規格一致的單精度浮點數。因此，在數值運算上，C應用了浮点数規範（浮點數加法、乘法等等）。

型別的約束程度以及評估方法，影響了語言的*型別*。更進一步，程式語言可能就型別多態性部分，對每一個型別都對應了一個極度個別的演算法的運算。型別理論研究型別系統，儘管實際的程式語言型別系統，起源於電腦架構的實際問題、編譯器實作，以及語言設計。

基礎
--

定型（*typing*，又稱型別）賦予一組位元某個意義。型別通常和記憶體中的數值或物件（如變數）相聯繫。因為在電腦中，任何數值都是以一組位元簡單組成的，硬體無法區分記憶體位址、指令碼、字元、整數、以及浮點數。型別可以告知程式和程式設計者，應該怎麼對待那些位元。

型別系統提供的主要功能有：

* **安全性**

使用型別可允許編譯器偵測無意義的，或者是可能無效的代碼。例如，可以識出一個無效的運算式`"Hello, World" + 3`，因為不能對（在平常的直覺中）逐字字串加上一個整數。強型別提供更多的安全性，但它並不能保證絕對安全（詳情請見型別安全）。
* **最佳化**

靜態型別檢查可提供有用的資訊給編譯器。例如，如果一個型別指明某個值必須以4的倍數對齊，編譯器就有可以使用更有效率的機器指令。
* **可讀性**

在更具表現力的型別系統中，若其可以闡明程式設計者的意圖的話，型別就可以充當為一種文件形式。例如，時間戳記可以是整數的子型別；但如果程式設計者宣告一個函式為返回一個時間戳記，而不只是一個整數，這個函式就能表現出一部分文件的闡釋性。
* **抽象化**（或**模組化**）

型別允許程式設計者對程式以較高層次的方式思考，而不是煩人的低層次實作。例如，程式設計者可以將字串想成一個值，以此取代僅僅是位元組的陣列。或者型別允許程式設計者表達兩個子系統之間的介面。將子系統間互動時的必要定義加以定位，防止子系統間的通訊發生衝突。
程式通常對每一個值關聯一個特定的型別（儘管一個型別可以有一個以上的子型別）。其它的實體，如物件、模組、通訊頻道、依賴關係，或者純粹的型別自己，可以和一個型別關聯。例如：

* 資料型別

一個數值的型別
* 類別

一個物件的型別
* 種類

一個型別的型別
在每一個程式語言中，都有一個特定的**型別系統**，保證程式的表現良好，並且排除違規的行為。**作用系統**對型別系統提供更多細微的控制。

类型
--

e : int：断言变量e的类型是int。

型別檢查
----

**型別檢查**所進行的檢驗處理以及實行型別的約束，可發生在編譯時期（靜態檢查）或執行時期（動態檢查）。靜態型別檢查是在編譯器所進行語義分析中進行的。如果一個語言強制實行型別規則（即通常只允許以不遺失資訊為前提的自動型別轉換）就稱此處理為**強型別**，反之稱為**弱型別**。

### 靜態和動態檢查

如果一個程式語言的型別檢查，可在不測試執行時期運算式的等價性的情況下進行，該語言即為**靜態型別**的。一個靜態型別的程式語言，是在執行時期和編譯時期之間的處理階段下重視這些區別的。如果程式的獨立模組，可進行各自的型別檢查（獨立編譯），而無須所有會在執行時出現的模組的那些資訊，該語言即具有一個編譯時期階段。如果一個程式語言支援執行時期（動態）調度已標記的資料，該語言即為**動態型別**的。如果一個程式語言破壞了階段的區別，因而型別檢查需要測試執行時期的運算式的等價性，該語言即為**依存型別**的。

在動態型別中，經常在執行時期進行型別標記的檢查，因為變數所約束的值，可經由執行路徑獲得不同的標記。在靜態型別程式語言中，型別標記使用辨識聯合型別表示。

動態型別經常出現於腳本語言和RAD語言中。動態型別在解释型语言中極為普遍，編譯語言則偏好無須執行時期標記的靜態型別。對於型別和隱式型別語言較完整的列表參見型別和隱式型別語言。

術語**推斷型別**（鸭子类型，duck typing）指的是動態型別在語言中的應用方式，它會「推斷」一個數值的型別。

看看型別標記檢查是如何運作的，考慮下列假碼範例：

```
**var** x; *//（1）*
x := 5; *//（2）*
x := "hi"; *//（3）*
```

在這個範例中，（1）宣告x；（2）將整數值5代給x；（3）將字串值"hi"代給x。在主要的靜態系統中，這個代碼片斷將會違反規則，因為（2）和（3）對 x所約束的型別相矛盾。

相較之下，一個純粹的動態型別系統允許上述程式的執行，因為型別標記附到*數值*上（不是*變數*）。在處理錯誤語句或運算式的時候，以動態型別實作的語言會捕捉程式的錯誤，而不是誤用錯誤型別的數值。換句話說，動態型別捕捉**在程式執行時**的錯誤。

典型的動態型別實作，會以型別標記維持程式所有數值的「標記」，並在運算任何數值之前檢查標記。例如：

```
**var** x := 5; *//（1）*
**var** y := "hi"; *//（2）*
**var** z := x + y; *//（3）*
```

在這個程式片斷中，（1）將數值5約束給x；（2）將數值"hi"約束給y；以及（3）嘗試將x加到y。在動態型別語言中，約束給x的值會是一對（整數, 5），且約束給y的值會是一對（字串, "hi"）。當這個程式嘗試執行第3行時，語言對型別標記整數和字串進行檢查，如果這兩個型別的+（加法）運算尚未定義，就會發出一個錯誤。

某些靜態語言有一個「後門」，在這些程式語言中，能夠編寫一些不被靜態型別所檢查的代碼。例如，Java和C-風格的語言有「轉型」可用。在靜態型別的程式語言中，不必然意味著缺乏動態型別機制。例如Java使用靜態型別，但某些運算需要支援執行時期的型別測試，這就是動態型別的一種形式。更多靜態和動態型別的討論，請參閱程式語言。

### 實踐中的靜態和動態型別檢查

對靜態型別和動態型別兩者之間的權衡也是必要的。

靜態型別在編譯時期時，就能可靠地發現型別錯誤。因此通常能增進最終程式的可靠性。然而，有多少的型別錯誤發生，以及有多少比例的錯誤能被靜態型別所捕捉，目前對此仍有爭論。靜態型別的擁護者認為，當程式通過型別檢查時，它才有更高的可靠性。雖然動態型別的擁護者指出，實際流通的軟體證明，兩者在可靠性上並沒有多大差別。可以認為靜態型別的價值，在於增進型別系統的強化。強型別語言（如ML和Haskell）的擁護者提出，幾乎所有的bug都可以看作是型別錯誤，如果編寫者以足夠恰當的方式，或者由編譯器推斷來宣告一個型別。

靜態型別通常可以編譯出速度較快的代碼。當編譯器清楚知道所要使用的資料型別，就可以產生最佳化過後的機器碼。更進一步，靜態型別語言中的編譯器，可以更輕易地發現較佳捷徑。某些動態語言（如Common Lisp）允許任意型別的宣告，以便於最佳化。以上理由使靜態型別更為普及。參閱最佳化。

相較之下，動態型別允許編譯器和解譯器更快速的運作。因為原始碼在動態型別語言中，變更為減少進行檢查，並減少解析代碼。這也可減少編輯－編譯－測試－除錯的週期。

靜態型別語言缺少型別推斷（如Java），而需要編寫者宣告所要使用的方法或函式的型別。編譯器將不允許編寫者忽略，這可為程式起附加性說明文件的作用。但靜態型別語言也可以無須型別宣告，所以與其說是靜態型別的代價，倒不如說是型別宣告的報酬。

靜態型別允許建構函式庫，它們的使用者不太可能意外的誤用。這可作為傳達函式庫開發者意圖的額外機制。

動態型別允許建構一些靜態型別系統所做不出來的東西。例如，*eval*函式，它使得執行任意資料作為代碼成為可能（不過其代碼的型別仍是靜態的）。此外，動態型別容納過渡代碼和原型設計，如允許使用字串代替資料結構。靜態型別語言最近的增強（如Haskell 一般化代數資料型別）允許eval函式以型別安全的方式撰寫。

動態型別使元程式設計更為強大，且更易於使用。例如C++模板的寫法，比起等價的Ruby或Python寫法要來的麻煩。更高度的執行時期構成物，如元類別（metaclass）和內觀（Introspection），對靜態型別語言而言通常更為困難。

### 強型別和弱型別

条目：強型別
**強型別**的基本定義即為，禁止錯誤型別的參數繼續運算。C語言的型別轉換即為缺乏強型別的證例；如果編寫者用C語言對一個值轉換型別，不僅令編譯器允許這個代碼，而且在執行時期中也同樣允許。這使得C代碼可更為緊密和快速，不過也使除錯變的更為困難。

部分學者使用術語**記憶體安全語言**（或簡稱為**安全語言**）形容禁止未定義運算發生的語言。例如，某個記憶體安全語言將會檢查陣列邊界。

**弱型別**意指一個語言可以隱式的轉換型別（或直接轉型）。看看先前的例子：

```
**var** x := 5;
**var** y := "37";
x + y;
```

在弱型別語言中編寫上述代碼，並不清楚將會得到哪一種結果。某些語言如Visual Basic，將會產生可以運作的代碼，它將會給出的結果是42：系統將字串"37"轉換成數字37，以符合運算上的直覺；其它的語言，像JavaScript將會產生的結果是"537"：系統將數字5轉換成字串"5"並把兩者串接起來。在Visual Basic和JavaScript中，最終的型別是以那兩個運算元為考量的規則所決定。在部分語言中，如AppleScript，某個值最終的型別，只以最左邊的運算元的型別所決定。

設計精巧的語言也允許語言顯現出弱型別（藉由类型推断之類的技術）的特性以方便使用，並且保留了強型別語言所提供的型別檢查和保護。例子包括VB.Net、C#以及Java。

運算子多載所帶來的簡化，像是不以算術運算中的加法來使用「+」，可以減少一些由動態型別所造成的混亂。例如，部分語言使用「.」或「&」來串連字串。

### 型別系統的安全性

条目：型別安全
程式語言的型別系統的第三種分類方法，就是型別運算和轉換的安全性。如果它不允許導致不正確的情況的運算或轉換，電腦科學就認為該語言是「型別安全」的。

再次看看這個假碼例子：

```
**var** x := 5;
**var** y := "37";
**var** z := x + y;
```

在一個如Visual Basic的語言中，例子中的變數z得到的值為42。不管編寫者有沒有這個意圖，該語言定義了明確的結果，且程式不會就此崩潰，或將不明定義的值賦給z。就這方面而言，這樣的語言就是型別安全的。

現在來看C的相同例子：

```
**int** x = 5;
**char** y[] = "37";
**char\*** z = x + y;
```

在這個例子中，z將會指向一個超過y位址5個位元組的記憶體位址，相當於指向y字串的指標之後的兩個空字元之處。這個位址的內容尚未定義，且有可能超出記憶體的定址界線，而且就這麼引用參考z會引起程式的終止。雖是一個良好型別，但卻不是記憶體安全的程式——如果以對型別安全語言而言不該發生為先決條件的話。

多態性和型別
------

条目：多型
術語「多態性」指的是：代碼（尤其是函式和類別）對各種型別的值能夠動作，或是相同資料結構的不同實體能夠控制不同型別的元素。為了提升複用代碼的潛在價值，型別系統逐漸允許多態性：在具有多態性的語言中，程式設計者只需要實作如列表或詞典的資料結構一次，而不是對使用到它的元素的每一個型別都規劃一次。基於這個原因，電腦學家也稱使用了一定的多態性的方法為**泛型程式設計**。型別理論的多態性基礎與抽象化、模組化和（偶爾）子型別有相當密切的聯繫關係。

### 推斷型別

条目：鸭子类型
推斷型別（鸭子类型，Duck typing）最初是由Dave Thomas在Ruby社群中提出的，推斷型別用了這個論證法「如果它像什麼，而且其它地方也像什麼，那麼它就是什麼。」

在某些程式設計環境中，兩個物件可以有相同的型別，即使它們沒有什麼交集。一個例子是C++中迭代器和指针所拥有的的雙重性，兩者皆以不甚相同的機制實作並提供一個\* 運算。

這個技術之所以常被稱作「鴨子型別」，是基於這句格言：「如果它搖搖擺擺的走法很像鴨子，而且它的嘎嘎叫聲也像鴨子，那它就是一隻鴨子！」

"If it waddles like a duck, and quacks like a duck, it's a duck!"

顯示宣告和隱式暗示
---------

許多靜態型別系統，如C和Java，要求要**宣告型別**：編寫者必須以指定型別明確地關聯到每一個變數上。其它的，如Haskell，則進行**型別推斷**：編譯器根據編寫者如何運用這些變數，以草擬出關於這個變數的型別的結論。例如，給定一個函式*f（x,y）*，它將*x*和*y*加起來，編譯器可以推斷出*x*和*y*必須是數字——因為加法僅定義給數字。因此，任何在其它地方以非數值型別（如字串或鏈表）作為參數來呼叫*f*的話，將會發出一個錯誤。

在代碼中數值、字串常數以及運算式，經常可以在詳細的前後文中暗示型別。例如，一個運算式`3.14`可暗示浮點數型別；而`[1, 2, 3]`則可暗示一個整數的鏈表；通常是一個陣列。

型別的型別
-----

*型別的型別*是一種**種類**。在型別程式設計中有明確的種類，如Haskell程式語言的*型別建構子*，在申請比較簡單的型別之後，其返回一個簡單的型別。例如，型別建構子*二選一*有這些種類*\* -> \* -> \**（*\**代表種類），而且它的申請*二選一字串整數*是一個簡單的型別。然而，大多數程式語言的型別，是由編寫者來暗示或*寫死*，這就並未將*種類*的概念用作為首選層。

型別可分為幾個大類：

* **原始型別**

這是最簡單的型別種類，例如：整數和浮點數
* **整數型別**

全部是數字的型別，例如：整數和自然數
* **浮點數型別**

以浮點數表示數字的型別
* **複合型別**

由基本型別組合成的型別，例如：陣列或記錄單元。抽象資料型別具有複合型別和界面兩種屬性，這取決於你提及哪一個。
* **子型別**
* **衍生型別**
* **物件型別**

例如：變數型別
* **不完全型別**
* **遞迴型別**
* **函式型別**

例如：二元函數
* **全稱量化型別**

如參數化型別、型別變數
* **存在量化型別**

如模組
* **精煉型別**

識別其它型別的子集的型別
* **依存型別**

取決於執行時期的數值的型別
* **所有權型別**

描述或約束物件導向系統結構的型別

相容性：等價性和子型別
-----------

對於靜態型別語言的型別檢查器，必須檢驗所有運算式的型別，是否與前後文所期望的型別一致。例如指派語句`x := *e*`，推斷運算式*e*的型別，必定與宣告或推斷的變數型別`x`一致。這個一致性的概念，就稱為*相容性*，是每一個程式語言所特有的。

很明顯，如果*e*和`x`的型別相同，就允許指派，然後這是一個有效的運算式。因此在最簡單的型別系統中，問題從兩個型別是否相容，簡化為兩個型別是否*相等*（或*等價*）。然而不同的語言對於兩個型別運算式是否理解為表示了相同型別，有著不同的標準。型別的*相等理論*的差異相當巨大，兩個極端的例子是*結構型別系統*（Structural type system），任兩個以相同結構所描述的值的型別都是等價的，且在*標明型別系統*（Nominative type system）上，沒有兩個獨特的語法構成的型別運算式表示同一型別，（*即*型別若要相等，就必須具有相同的「名字」）。

在子型別的語言中，相容關係更加複雜。特別是如果*A*是*B*的子型別，那麼型別*A*的值可用於型別*B*也屬意料之中，但反過來就不是這樣。如同等價性，對每一個程式語言而言，子型別的關係的定義是不同的，可能存在各種變化。在語言中出現的參數或者特定的多態性，也可能意味著具有對型別的相容性。

爭議
--

在強型別、靜態型別語言的支持者，和動態型別、自由形式的支持者之間，經常發生爭執。前者主張，在編譯的時候就可以較早發現錯誤，而且還可增進執行時期的效能。後者主張，使用更加動態的型別系統，分析程式碼更為簡單，減少出錯機會，才能更加輕鬆快速的編寫程式。與此相關的是，考慮到在型別推斷的程式語言中，通常不需要手動宣告型別，這部分的額外開銷也就自動降低了。

参考文献
----

1. ↑  Harper, Robert & Benjamin C. Pierce (2005), "Design Considerations for ML-Style Module Systems", in Pierce, Benjamin C. *Advanced Topics in Types and Programming Languages*, Cambridge, MA: MIT Press, ISBN 0262162288 （页面存档备份，存于）
2. ↑ .  [2007-03-24]. （原始内容存档于2008-05-12）.
3. ↑  Meijer, Erik and Peter Drayton.  (PDF). Microsoft Corporation. （原始内容 (PDF)存档于2007-02-16）.

參閱
--

|  |  |
| --- | --- |
|  | 維基教科書中的相關電子：**Types** |

|  |  |
| --- | --- |
|  | 維基教科書中的相關電子：**Class Declarations** |

* 運算子多載
* 多型 (電腦科學)
* 程式語言
* Type signature
* Signedness
* 型別系統參考表

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
数据类型 |
| --- |
|  |
| 无解释的 | 
* 位元
* 字节
* 三进制位
* 三进制字节
* Word

 |
|  |
| 数值 | 
* 整数
	+ 符号性
	+ 有符号数
	+ 无符号数
* 定点数
* 浮点数
	+ 双精度
	+ 扩展精度（英语：）
	+ 半精度
	+ 迷你浮点数（英语：）
	+ 八倍精度（英语：）
	+ 四倍精度（英语：）
	+ 单精度
* 有理数（英语：）
* 复数（英语：）
* 任意精度算术
* 区间（英语：）

 |
|  |
| 文本 | 
* 字符
* 字符串

 |
|  |
| 指针 | 
* 記憶體位址
	+ 物理地址
	+ 虚拟地址
* 參照

 |
|  |
| 组合 | 
* 代数数据类型
	+ 广义（英语：）
* 数组
* 关联数组
* 类
* 串列
* 对象
	+ 元对象
* 可选类型
* 积类型（英语：）
* 记录
* 集合
* 联合体
	+ 标签

 |
|  |
| 其他 | 
* 布尔型
* 底层类别（英语：）
* 容器
* 枚举类型
* 异常
* 一等函数
* 不透明数据类型（英语：）
* 递归数据类型
* 信号标
* 字串流
* 顶类型（英语：）
* 类型类
* 類型系統
* 单位类型（英语：）
* Void
* 不定型別

 |
|  |
| 相关议题 | 
* 抽象資料型別
* 数据结构
* 介面
* 种类（英语：）
* 元类
* 对象类型（英语：）
* 原始型別与複合型別
* 协议
* 子类型
* C++模板
* 型別構造器
* 参数多态

 |

 |