查看“︁類型系統”︁的源代码
←
類型系統
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{NoteTA| |T=zh-tw:型別系統;zh-cn:类型系统; |G1=IT |1=zh-cn:类型;zh-tw:型別 |2=zh-cn:表达式;zh-tw:運算式 |3=zh-cn:函数;zh-tw:函式 |5=zh-cn:运行;zh-tw:執行 }}{{Type systems}} 在[[计算机科学]]中,'''类型系統'''({{lang-en|type system}})用于定義如何將[[程式語言]]中的[[數值]]和[[運算式]]归類为许多不同的'''[[型別]]''',如何操作这些型別,这些型別如何互相作用。型別可以确认一个值或者一组值具有特定的意义和目的(雖然某些型別,如抽象型別和函式型別,在程式執行中,可能不表示為值)。型別系統在各種語言之間有非常大的不同,也許,最主要的差異存在於編譯時期的語法,以及執行時期的操作实现方式。 [[編譯器]]可能使用值的靜態型別以最佳化所需的儲存區,並選取對值運算時的較佳演算法。例如,在許多[[C语言|C]]編譯器中,「浮點數」[[資料型別]]是以32 [[位元]]表示,與[[IEEE 754|IEEE 754規格]]一致的單精度浮點數。因此,在數值運算上,C應用了[[浮点数]]規範(浮點數加法、乘法等等)。 型別的約束程度以及評估方法,影響了語言的''型別''。更進一步,程式語言可能就[[型別多態性]]部分,對每一個型別都對應了一個極度個別的演算法的運算。[[型別理論]]研究型別系統,儘管實際的程式語言型別系統,起源於電腦架構的實際問題、編譯器實作,以及語言設計。 ==基礎== 定型(''typing'',又稱型別-{指派}-)賦予一組[[位元]]某個意義。型別通常和[[電腦記憶體|記憶體]]中的數值或[[物件 (電腦科學)|物件]](如[[變數]])相聯繫。因為在[[電腦]]中,任何數值都是以一組位元簡單組成的,硬體無法區分[[記憶體位址]]、[[指令碼]]、[[字元]]、[[整數]]、以及[[浮點數]]。型別可以告知程式和程式設計者,應該怎麼對待那些位元。 型別系統提供的主要功能有: * '''安全性''' :使用型別可允許[[編譯器]]偵測無意義的,或者是可能無效的代碼。例如,可以識出一個無效的運算式<code>"Hello, World" + 3</code>,因為不能對(在平常的直覺中)[[逐字字串]]加上一個[[整數]]。強型別提供更多的安全性,但它並不能保證絕對安全(詳情請見[[型別安全]])。 * '''最佳化''' :靜態型別檢查可提供有用的資訊給編譯器。例如,如果一個型別指明某個值必須以4的倍數對齊,編譯器就有可以使用更有效率的機器指令。 * '''可讀性''' :在更具表現力的型別系統中,若其可以闡明程式設計者的意圖的話,型別就可以充當為一種[[文件]]形式。例如,時間戳記可以是整數的子型別;但如果程式設計者宣告一個函式為返回一個時間戳記,而不只是一個整數,這個函式就能表現出一部分文件的闡釋性。 * '''抽象化'''(或'''模組化''') :型別允許程式設計者對程式以較高層次的方式思考,而不是煩人的低層次實作。例如,程式設計者可以將字串想成一個值,以此取代僅僅是位元組的陣列。或者型別允許程式設計者表達兩個子系統之間的[[介面 (程式設計)|介面]]。將子系統間互動時的必要定義加以定位,防止子系統間的通訊發生衝突。 程式通常對每一個值關聯一個特定的型別(儘管一個型別可以有一個以上的[[子型別]])。其它的實體,如[[物件 (電腦科學)|物件]]、[[模块化编程|模組]]、通訊頻道、依賴關係,或者純粹的型別自己,可以和一個型別關聯。例如: * [[資料型別]] :一個數值的型別 * [[類別]] :一個物件的型別 * [[種類]] :一個型別的型別 在每一個程式語言中,都有一個特定的'''型別系統''',保證程式的表現良好,並且排除違規的行為。'''[[作用系統]]'''對型別系統提供更多細微的控制。 == 类型 == <math>e : int</math>:断言变量e的类型是int。 ==型別檢查== '''型別檢查'''所進行的檢驗處理以及實行型別的約束,可發生在[[編譯時期]](靜態檢查)或[[執行時期]](動態檢查)。靜態型別檢查是在[[編譯器]]所進行[[語義分析]]中進行的。如果一個語言強制實行型別規則(即通常只允許以不遺失資訊為前提的自動型別轉換)就稱此處理為'''強型別''',反之稱為'''弱型別'''。 ===靜態和動態檢查=== 如果一個程式語言的型別檢查,可在不測試執行時期運算式的等價性的情況下進行,該語言即為'''靜態型別'''的。一個靜態型別的程式語言,是在執行時期和編譯時期之間的處理階段下重視這些區別的。如果程式的獨立模組,可進行各自的型別檢查([[獨立編譯]]),而無須所有會在執行時出現的模組的那些資訊,該語言即具有一個編譯時期階段。如果一個程式語言支援執行時期(動態)調度已標記的資料,該語言即為'''動態型別'''的。如果一個程式語言破壞了階段的區別,因而型別檢查需要測試執行時期的運算式的等價性,該語言即為'''依存型別'''的。<ref>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 [http://en.wikipedia.org/w/index.php?title=Special:Booksources&isbn=0262162288 0262162288] {{Wayback|url=http://en.wikipedia.org/w/index.php?title=Special:Booksources&isbn=0262162288 |date=20200409062406 }}</ref> 在動態型別中,經常在[[執行時期]]進行型別標記的檢查,因為變數所約束的值,可經由[[執行 (電腦)|執行]]路徑獲得不同的標記。在靜態型別程式語言中,型別標記使用[[辨識聯合]]型別表示。 動態型別經常出現於[[腳本語言]]和[[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的值會是一對<tt>([[整數]], 5)</tt>,且約束給y的值會是一對<tt>([[字串]], "hi")</tt>。當這個程式嘗試執行第3行時,語言對型別標記<tt>整數</tt>和<tt>字串</tt>進行檢查,如果這兩個型別的<tt>+</tt>(加法)運算尚未定義,就會發出一個錯誤。 某些靜態語言有一個「後門」,在這些程式語言中,能夠編寫一些不被靜態型別所檢查的代碼。例如,Java和C-風格的語言有「[[cast (computer science)|轉型]]」可用。在靜態型別的程式語言中,不必然意味著缺乏動態型別機制。例如Java使用靜態型別,但某些運算需要支援執行時期的型別測試,這就是動態型別的一種形式。更多靜態和動態型別的討論,請參閱[[程式語言]]。 ===實踐中的靜態和動態型別檢查=== 對靜態型別和動態型別兩者之間的[[權衡]]也是必要的。 靜態型別在編譯時期時,就能可靠地發現型別錯誤。因此通常能增進最終程式的可靠性。然而,有多少的型別錯誤發生,以及有多少比例的錯誤能被靜態型別所捕捉,目前對此仍有爭論。靜態型別的擁護者認為,當程式通過型別檢查時,它才有更高的可靠性。雖然動態型別的擁護者指出,實際流通的軟體證明,兩者在可靠性上並沒有多大差別。可以認為靜態型別的價值,在於增進型別系統的強化。強型別語言(如[[ML語言|ML]]和[[Haskell]])的擁護者提出,幾乎所有的bug都可以看作是型別錯誤,如果編寫者以足夠恰當的方式,或者由編譯器推斷來宣告一個型別。<ref>{{Cite web |url=http://citeseer.ist.psu.edu/xi98dependent.html |title=存档副本 |access-date=2007-03-24 |archive-date=2008-05-12 |archive-url=https://web.archive.org/web/20080512140538/http://citeseer.ist.psu.edu/xi98dependent.html |dead-url=no }}</ref> 靜態型別通常可以編譯出速度較快的代碼。當編譯器清楚知道所要使用的資料型別,就可以產生最佳化過後的機器碼。更進一步,靜態型別語言中的編譯器,可以更輕易地發現較佳捷徑。某些動態語言(如[[Common Lisp]])允許任意型別的宣告,以便於最佳化。以上理由使靜態型別更為普及。參閱[[最佳化]]。 相較之下,動態型別允許編譯器和解譯器更快速的運作。因為原始碼在動態型別語言中,變更為減少進行檢查,並減少解析代碼。這也可減少編輯-編譯-測試-除錯的週期。 靜態型別語言缺少型別推斷(如Java),而需要編寫者宣告所要使用的方法或函式的型別。編譯器將不允許編寫者忽略,這可為程式起附加性說明文件的作用。但靜態型別語言也可以無須型別宣告,所以與其說是靜態型別的代價,倒不如說是型別宣告的報酬。 靜態型別允許建構函式庫,它們的使用者不太可能意外的誤用。這可作為傳達函式庫開發者意圖的額外機制。 動態型別允許建構一些靜態型別系統所做不出來的東西。例如,''[[eval]]''函式,它使得執行任意資料作為代碼成為可能(不過其代碼的型別仍是靜態的)。此外,動態型別容納過渡代碼和原型設計,如允許使用字串代替資料結構。靜態型別語言最近的增強(如Haskell [[GADT|一般化代數資料型別]])允許eval函式以型別安全的方式撰寫。 動態型別使[[元程式設計]]更為強大,且更易於使用。例如[[C++]]模板的寫法,比起等價的[[Ruby]]或[[Python]]寫法要來的麻煩。更高度的執行時期構成物,如[[元類別]](metaclass)和[[內觀]](Introspection),對靜態型別語言而言通常更為困難。 ===強型別和弱型別=== {{main|強型別}} '''強型別'''的基本定義即為,禁止錯誤型別的參數繼續運算。[[C語言]]的[[型別轉換]]即為缺乏強型別的證例;如果編寫者用C語言對一個值轉換型別,不僅令編譯器允許這個代碼,而且在執行時期中也同樣允許。這使得C代碼可更為緊密和快速,不過也使[[除錯]]變的更為困難。 部分學者使用術語'''記憶體安全語言'''(或簡稱為'''安全語言''')形容禁止未定義運算發生的語言。例如,某個記憶體安全語言將會[[邊界檢查|檢查陣列邊界]]。 '''弱型別'''意指一個語言可以隱式的轉換型別(或直接轉型)。看看先前的例子: '''var''' x := 5; '''var''' y := "37"; x + y; 在弱型別語言中編寫上述代碼,並不清楚將會得到哪一種結果。某些語言如[[Visual Basic]],將會產生可以運作的代碼,它將會給出的結果是42:系統將字串"37"轉換成數字37,以符合運算上的直覺;其它的語言,像[[JavaScript]]將會產生的結果是"537":系統將數字5轉換成字串"5"並把兩者串接起來。在Visual Basic和JavaScript中,最終的型別是以那兩個[[運算元]]為考量的規則所決定。在部分語言中,如[[AppleScript]],某個值最終的型別,只以最左邊的運算元的型別所決定。 設計精巧的語言也允許語言顯現出弱型別(藉由[[类型推断]]之類的技術)的特性以方便使用,並且保留了強型別語言所提供的型別檢查和保護。例子包括[[Visual Basic .NET|VB.Net]]、[[C Sharp|C#]]以及[[Java]]。 [[運算子多載]]所帶來的簡化,像是不以算術運算中的加法來使用「+」,可以減少一些由動態型別所造成的混亂。例如,部分語言使用「.」或「&」來串連字串。 ===型別系統的安全性=== {{main|型別安全}} 程式語言的型別系統的第三種分類方法,就是型別運算和轉換的安全性。如果它不允許導致不正確的情況的運算或轉換,電腦科學就認為該語言是「型別安全」的。 再次看看這個假碼例子: '''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會引起程式的終止。雖是一個良好型別,但卻不是記憶體安全的程式——如果以對型別安全語言而言不該發生為先決條件的話。 ==多態性和型別== {{main|多型}} 術語「多態性」指的是:代碼(尤其是函式和類別)對各種型別的值能夠動作,或是相同資料結構的不同實體能夠控制不同型別的元素。為了提升複用代碼的潛在價值,型別系統逐漸允許多態性:在具有多態性的語言中,程式設計者只需要實作如列表或詞典的資料結構一次,而不是對使用到它的元素的每一個型別都規劃一次。基於這個原因,電腦學家也稱使用了一定的多態性的方法為'''[[泛型程式設計]]'''。型別理論的多態性基礎與[[抽象化]]、[[模块化编程|模組化]]和(偶爾)[[子型別]]有相當密切的聯繫關係。 ===推斷型別=== {{main|鸭子类型}} 推斷型別([[鸭子类型]],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''的話,將會發出一個錯誤。 在代碼中數值、字串常數以及運算式,經常可以在詳細的前後文中暗示型別。例如,一個運算式<code>3.14</code>可暗示[[浮點數]]型別;而<code>[1, 2, 3]</code>則可暗示一個整數的鏈表;通常是一個[[陣列]]。 ==型別的型別== ''型別的型別''是一種'''種類'''。在[[型別程式設計]]中有明確的種類,如[[Haskell|Haskell程式語言]]的''型別建構子'',在申請比較簡單的型別之後,其返回一個簡單的型別。例如,型別建構子''二選一''有這些種類''* -> * -> *''(''*''代表種類),而且它的申請''二選一字串整數''是一個簡單的型別。然而,大多數程式語言的型別,是由編寫者來暗示或''寫死'',這就並未將''種類''的概念用作為首選層。 型別可分為幾個大類: * '''[[原始型別]]''' :這是最簡單的型別種類,例如:[[整数 (计算机科学)|整數]]和[[浮點數]] * '''[[整数 (计算机科学)|整數型別]]''' :全部是數字的型別,例如:整數和自然數 * '''[[浮點數|浮點數型別]]''' :以浮點數表示數字的型別 * '''[[複合型別]]''' :由基本型別組合成的型別,例如:[[陣列]]或[[物件 (電腦科學)|記錄單元]]。[[抽象資料型別]]具有複合型別和界面兩種屬性,這取決於你提及哪一個。 * '''[[子型別]]''' * '''[[衍生型別]]''' * '''[[物件型別]]''' :例如:[[變數型別]] * '''[[不完全型別]]''' * '''[[遞迴型別]]''' * '''[[函式型別]]''' :例如:二元函數 * '''[[全稱量化]]型別''' :如[[參數化型別]]、[[型別變數]] * '''[[存在量化]]型別''' :如[[模組 (程式設計)|模組]] * '''[[精煉型別]]''' :識別其它型別的子集的型別 * '''[[依存型別]]''' :取決於執行時期的數值的型別 * '''[[所有權型別]]''' :描述或約束物件導向系統結構的型別 ==相容性:等價性和子型別== 對於靜態型別語言的型別檢查器,必須檢驗所有[[運算式]]的型別,是否與前後文所期望的型別一致。例如[[指派]]語句<code>x := ''e''</code>,推斷運算式''e''的型別,必定與宣告或推斷的變數型別<code>x</code>一致。這個一致性的概念,就稱為''相容性'',是每一個程式語言所特有的。 很明顯,如果''e''和<code>x</code>的型別相同,就允許指派,然後這是一個有效的運算式。因此在最簡單的型別系統中,問題從兩個型別是否相容,簡化為兩個型別是否''相等''(或''等價'')。然而不同的語言對於兩個型別運算式是否理解為表示了相同型別,有著不同的標準。型別的''相等理論''的差異相當巨大,兩個極端的例子是''[[結構型別系統]]''(Structural type system),任兩個以相同結構所描述的值的型別都是等價的,且在''[[標明型別系統]]''(Nominative type system)上,沒有兩個獨特的語法構成的型別運算式表示同一型別,(''即''型別若要相等,就必須具有相同的「名字」)。 在[[子型別]]的語言中,相容關係更加複雜。特別是如果''A''是''B''的子型別,那麼型別''A''的值可用於型別''B''也屬意料之中,但反過來就不是這樣。如同等價性,對每一個程式語言而言,子型別的關係的定義是不同的,可能存在各種變化。在語言中出現的參數或者特定的[[多態性]],也可能意味著具有對型別的相容性。 ==爭議== 在強型別、靜態型別語言的支持者,和動態型別、自由形式的支持者之間,經常發生爭執。前者主張,在編譯的時候就可以較早發現錯誤,而且還可增進執行時期的效能。後者主張,使用更加動態的型別系統,分析程式碼更為簡單,減少出錯機會,才能更加輕鬆快速的編寫程式。<ref>{{cite web | url=http://pico.vub.ac.be/~wdmeuter/RDL04/papers/Meijer.pdf | title=Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages | author=Meijer, Erik and Peter Drayton | publisher=[[Microsoft]] Corporation | deadurl=yes | archiveurl=https://web.archive.org/web/20070216025556/http://pico.vub.ac.be/~wdmeuter/RDL04/papers/Meijer.pdf | archivedate=2007-02-16 }}</ref>與此相關的是,考慮到在型別推斷的程式語言中,通常不需要手動宣告型別,這部分的額外開銷也就自動降低了。 == 参考文献 == {{Reflist|32em}} ==參閱== {{wikibooks|Ada Programming|Types}} {{wikibooks|Haskell|Class Declarations}} * [[運算子多載]] * [[多型 (電腦科學)]] * [[程式語言]] * [[Type signature]] * [[Signedness]] * [[Template:型別系統參考表|型別系統參考表]] {{数据类型}} [[Category:数据类型]] [[Category:类型论|型別理論]]
该页面使用的模板:
Template:Cite web
(
查看源代码
)
Template:Lang-en
(
查看源代码
)
Template:Main
(
查看源代码
)
Template:NoteTA
(
查看源代码
)
Template:Reflist
(
查看源代码
)
Template:Type systems
(
查看源代码
)
Template:Wayback
(
查看源代码
)
Template:Wikibooks
(
查看源代码
)
Template:数据类型
(
查看源代码
)
返回
類型系統
。
导航菜单
个人工具
登录
命名空间
页面
讨论
不转换
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
工具
链入页面
相关更改
页面信息