類型系統

類型系統

類型系統是在計算機科學中,類型系統用於定義如何將程式語言中的數值表達式歸類為許多不同的類型,如何操作這些類型,這些類型如何互相作用。類型可以確認一個值或者一組值具有特定的意義和目的(雖然某些類型,如抽象類型和函式類型,在程式運行中,可能不表示為值)。類型系統在各種語言之間有非常大的不同,也許,最主要的差異存在於編譯時期的語法,以及運行時期的操作實現方式。

基本介紹

  • 中文名:類型系統
  • 外文名:The type system
  • 基礎存儲器中的數值或對象相聯繫
  • 最佳化:可提供有用的信息給編譯器
解析,基礎,類型檢查,動態檢查,檢查,強類型弱類型,安全性,多態性和類型,顯示聲明,隱式暗示,類型的類型,兼容性,爭議,

解析

編譯器可能使用值的靜態類型以最佳化所需的存儲區,並選取對值運算時的較佳算法。例如,在許多C編譯器中,“浮點數數據類型是以 32 比特表示,與IEEE 754 規格一致的單精度浮點數。因此,在數值運算上,C 套用了浮點數規範(浮點數加法、乘法等等)。
類型的約束程度以及評估方法,影響了語言的類型。更進一步,程式語言可能就類型多態性部分,對每一個類型都對應了一個極度個別的算法的運算。類型理論研究類型系統,儘管實際的程式語言類型系統,起源於電腦架構的實際問題、編譯器實現,以及語言設計。

基礎

定型(typing,又稱類型指派)賦予一組比特某個意義。類型通常和存儲器中的數值或對象(如變數)相聯繫。因為在電腦中,任何數值都是以一組比特簡單組成的,硬體無法區分存儲器地址腳本字元整數、以及浮點數。類型可以告知程式和程式設計者,應該怎么對待那些比特。
類型系統提供的主要功能有:
  • 安全性
使用類型可允許編譯器偵測無意義的,或者是可能無效的代碼。例如,可以識出一個無效的表達式 "Hello, World" + 3,因為不能對(在平常的直覺中)逐字字元串加上一個整數強類型提供更多的安全性,但它並不能保證絕對安全(詳情請見類型安全
  • 最佳化
靜態類型檢查可提供有用的信息給編譯器。例如,如果一個類型指明某個值必須以 4 的倍數對齊,編譯器就有可以使用更有效率的機器指令
  • 可讀性
在更具表現力的類型系統中,若其可以闡明程式設計者的意圖的話,類型就可以充當為一種檔案形式。例如,時間戳記可以是整數的子類型;但如果程式設計者聲明一個函式為返回一個時間戳記,而不只是一個整數,這個函式就能表現出一部分檔案的闡釋性。
  • 抽象化(或模組化
類型允許程式設計者對程式以較高層次的方式思考,而不是煩人的低層次實現。例如,程式設計者可以將字元串想成一個值,以此取代僅僅是位元組的數組。或者類型允許程式設計者表達兩個子系統之間的接口。將子系統間互動時的必要定義加以定位,防止子系統間的通信發生衝突。
程式通常對每一個值關係一個特定的類型(儘管一個類型可以有一個以上的子類型)。其它的實體,如對象模組、通信頻道、依賴關係,或者純粹的類型自己,可以和一個類型關係。例如:
一個數值的類型
一個對象的類型
一個類型的類型
在每一個程式語言中,都有一個特定的類型系統,保證程式的表現良好,並且排除違規的行為。作用系統對類型系統提供更多細微的控制。

類型檢查

類型檢查所進行的檢驗處理以及實行類型的約束,可發生在編譯時期(靜態檢查)或運行時期(動態檢查)。靜態類型檢查是在編譯器所進行語義分析中進行的。如果一個語言強制實行類型規則(即通常只允許以不丟失信息為前提的自動類型轉換)就稱此處理為強類型,反之稱為弱類型

動態檢查

如果一個程式語言的類型檢查,可在不測試運行時期表達式的等價性的情況下進行,該語言即為靜態類型的。一個靜態類型的程式語言,是在運行時期和編譯時期之間的處理階段下重視這些區別的。如果程式的獨立模組,可進行各自的類型檢查(獨立編譯),而無須所有會在運行時出現的模組的那些信息,該語言即具有一個編譯時期階段。如果一個程式語言支持運行時期(動態)調度已標記的數據,該語言即為動態類型的。如果一個程式語言破壞了階段的區別,因而類型檢查需要測試運行時期的表達式的等價性,該語言即為依存類型的。
在動態類型中,經常在運行時期進行類型標記的檢查,因為變數所約束的值,可經由運行路徑獲得不同的標記。在靜態類型程式語言中,類型標記使用辨識聯合類型表示。
動態類型經常出現於腳本語言RAD語言中。動態類型在解譯語言中極為普遍,編譯語言則偏好無須運行時期標記的靜態類型。對於類型和隱式類型語言較完整的列表參見類型和隱式類型語言。
術語推斷類型鴨子類型,duck typing)指的是動態類型在語言中的套用方式,它會“推斷”一個數值的類型。
某些靜態語言有一個“後門”,在這些程式語言中,能夠編寫一些不被靜態類型所檢查的代碼。例如,Java 和 C-風格的語言有“轉型”可用。在靜態類型的程式語言中,不必然意味著缺乏動態類型機制。例如 Java 使用靜態類型,但某些運算需要支持運行時期的類型測試,這就是動態類型的一種形式。更多靜態和動態類型的討論,請參閱程式語言。

檢查

對靜態類型和動態類型兩者之間的權衡也是必要的。
靜態類型在編譯時期時,就能可靠地發現類型錯誤。因此通常能增進最終程式的可靠性。然而,有多少的類型錯誤發生,以及有多少比例的錯誤能被靜態類型所捕捉,仍有爭論。靜態類型的擁護者認為,當程式通過類型檢查時,它才有更高的可靠性。雖然動態類型的擁護者指出,實際流通的軟體證明,兩者在可靠性上並沒有多大差別。可以認為靜態類型的價值,在於增進類型系統的強化。強類型語言(如 MLHaskell)的擁護者提出,幾乎所有的臭蟲都可以看作是類型錯誤,如果編寫者以足夠恰當的方式,或者由編譯器推斷來聲明一個類型。
靜態類型通常可以編譯出速度較快的代碼。當編譯器清楚知道所要使用的數據類型,就可以產生最佳化過後的機器碼。更進一步,靜態類型語言中的編譯器,可以更輕易地發現較佳捷徑。某些動態語言(如 Common Lisp)允許任意類型的聲明,以便於最佳化。以上理由使靜態類型更為普及。參閱最佳化
相較之下,動態類型允許編譯器和解譯器更快速的運作。因為原始碼在動態類型語言中,變更為減少進行檢查,並減少解析代碼。這也可減少編輯-編譯-測試-除錯的周期。
靜態類型語言缺少類型推斷(如 Java),而需要編寫者聲明所要使用的方法或函式的類型。編譯器將不允許編寫者忽略,這可為程式起附加性說明檔案的作用。但靜態類型語言也可以無須類型聲明,所以與其說是靜態類型的代價,倒不如說是類型聲明的報酬。
靜態類型允許構造函式館,它們的用戶不太可能意外的誤用。這可作為傳達庫開發者意圖的額外機制。
動態類型允許建構一些靜態類型系統所做不出來的東西。例如,eval 函式,它使得運行任意數據作為代碼成為可能(不過其代碼的類型仍是靜態的)。此外,動態類型容納過渡代碼和原型設計,如允許使用字元串代替數據結構。靜態類型語言最近的增強(如 Haskell 一般化代數數據類型)允許 eval 函式以類型安全的方式撰寫。
動態類型使元程式設計更為強大,且更易於使用。例如 C++ 模板的寫法,比起等價的 RubyPython 寫法要來的麻煩。更高度的運行時期構成物,如元類型(metaclass)和內觀(Introspection),對靜態類型語言而言通常更為困難。

強類型弱類型

強類型的基本定義即為,禁止錯誤類型的參數繼續運算。C語言的類型轉換即為缺乏強類型的證例;如果編寫者用 C 語言對一個值轉換類型,不僅令編譯器允許這個代碼,而且在運行時期中也同樣允許。這使得 C 代碼可更為緊密和快速,不過也使除錯變的更為困難。
部分學者使用術語存儲器安全語言(或簡稱為安全語言)形容禁止未定義運算發生的語言。例如,某個存儲器安全語言將會檢查數組邊界。
設計精巧的語言也允許語言顯現出弱類型(藉由類型推斷之類的技術)的特性以方便使用,並且保留了強類型語言所提供的類型檢查和保護。例子包括 VBNet、C# 以及 Java
運算符重載所帶來的簡化,像是不以算術運算中的加法來使用“+”,可以減少一些由動態類型所造成的混亂。例如,部分語言使用“.”或“&”來串連字元串。

安全性

程式語言的類型系統的第三種分類方法,就是類型運算和轉換的安全性。如果它不允許導致不正確的情況的運算或轉換,計算機科學就認為該語言是“類型安全”的。

多態性和類型

術語“多態性”指的是:代碼(尤其是函式和類型)對各種類型的值能夠動作,或是相同數據結構的不同實體能夠控制不同類型的元素。為了提升復用代碼的潛在價值,類型系統逐漸允許多態性:在具有多態性的語言中,程式設計者只需要實現如列表或詞典的數據結構一次,而不是對使用到它的元素的每一個類型都規劃一次。基於這個原因,電腦學家也稱使用了一定的多態性的方法為泛型程式設計。類型理論的多態性基礎與抽象化模組化和(偶爾)子類型有相當密切的聯繫關係。
推斷類型
推斷類型(鴨子類型,Duck typing)最初是由 Dave Thomas 在 Ruby 社區中提出的,推斷類型用了這個論證法“如果它像什麼,而且其它地方也像什麼,那么它就是什麼。”
在某些程式設計環境中,兩個對象可以有相同的類型,即使它們沒有什麼交集。一個例子是 C++疊代器和指針之間的雙重性。兩者皆以不甚相同的機制實現並提供一個 * 運算。
這個技術之所以常被稱作“鴨子類型”,是基於這句格言:“如果它搖搖擺擺的走法很像鴨子,而且它的嘎嘎叫聲也像鴨子,那它就是一隻鴨子!”

顯示聲明

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

隱式暗示

在代碼中數值、字元串常數以及表達式,經常可以在詳細的前後文中暗示類型。例如,一個表達式 3.14 可暗示浮點數類型;而 [1, 2, 3] 則可暗示一個整數的鍊表;通常是一個數組

類型的類型

類型的類型是一種種類。在類型程式設計中有明確的種類,如 Haskell 程式語言的類型構造函式,在申請比較簡單的類型之後,其返回一個簡單的類型。例如,類型構造函式 二選一 有這些種類 * -> * -> ** 代表種類),而且它的申請 二選一 字元串 整數 是一個簡單的類型。然而,大多數程式語言的類型,是由編寫者來暗示或硬編碼,這就並未將種類的概念用作為首選層。
類型可分為幾個大類:
這是最簡單的類型種類,例如:整數浮點數
  • 整數類型
全部是數字的類型,例如:整數和自然數
  • 浮點數類型
浮點數表示數字的類型
由基本類型組合成的類型,例如:數組或記錄單元。抽象數據類型具有複合類型和界面兩種屬性,這取決於你提及哪一個。
例如:變數類型
例如:雙參函式
如參數化類型、類型變數
  • 精煉類型
識別其它類型的子集的類型
  • 依存類型
取決於運行時期的數值的類型
  • 所有權類型
描述或約束面向對象系統結構的類型

兼容性

對於靜態類型語言的類型檢查器,必須檢驗所有表達式的類型,是否與前後文所期望的類型一致。例如指派語句x := e,推斷表達式 e 的類型,必定與聲明或推斷的變數類型 x 一致。這個一致性的概念,就稱為兼容性,是每一個程式語言所特有的。
很明顯,如果 e 和 x 的類型相同,就允許指派,然後這是一個有效的表達式。因此在最簡單的類型系統中,問題從兩個類型是否兼容,簡化為兩個類型是否相等(或等價)。然而不同的語言對於兩個類型表達式是否理解為表示了相同類型,有著不同的標準。類型的相等理論的差異相當巨大,兩個極端的例子是結構類型系統(Structural type system),任兩個以相同結構所描述的值的類型都是等價的,且在標明類型系統(Nominative type system)上,沒有兩個獨特的語法構成的類型表達式表示同一類型,(類型若要相等,就必須具有相同的“名字”)。
子類型的語言中,兼容關係更加複雜。特別是如果 AB 的子類型,那么類型 A 的值可用於類型 B 也屬意料之中,但反過來就不是這樣。如同等價性,對每一個程式語言而言,子類型的關係的定義是不同的,可能存在各種變化。在語言中出現的參數或者特定的多態性,也可能意味著具有對類型的兼容性。

爭議

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

相關詞條

熱門詞條

聯絡我們