本節書摘來華章計算機《sql與關系資料庫理論——如何編寫健壯的sql代碼》一書中的第2章 ,第2.4節 c. j. date 著 單世民 何英昊 許侃 譯 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
現在開始,我要用“類型”代替“域”了。那麼到底類型是什麼?實質上,它就是已命名的、有限的值集合——某種特定類型的所有可能取值。比如,所有可能的整數,所有可能的字元串,所有可能的供應商編号,所有可能的xml文檔,或者所有可能的帶有某一标題的關系等等。簡要地說:
我們所關心的類型都是有限的,因為我們使用的是計算機,這是根據定義就已經确定是有限的了(就像在本章先前在類型部分提到過的那樣)。
還要注意限定詞“已命名的”:不同名稱的類型是不同類型。
另外:
每個值總是某種類型的值——事實上,就是某一種類型的,除非有對類型繼承的支援。類型繼承的概念超出了本書的範圍。注意:因為沒有值能夠同時屬于兩種類型,是以類型根據定義是互斥(不重疊)的。不過,我或許需要對此簡要叙述一下。正像本章的一位評審所說的,恒溫動物類型和四足動物類型不是重疊的麼?它們确實重疊,不過我要說的是如果類型之間重疊,那麼就有一堆原因促使我們探究類型繼承——實際上,是探究所謂的多繼承。因為那些原因(實際上就是繼承的整個主題)與我們所處的上下文無關,是以我不打算在此書中對其進行讨論。
每個變量、每個屬性、每個傳回一個結果的運算符,以及每個運算符的每個參數都要聲明為某種類型。注8也就是說,如果變量v聲明為類型t則表示-每個可被合法指派給v的值v都應是類型為t的值。
每個表達式都表示某個值,并且也是以是屬于某種類型的,即,目前正在讨論的那個值的類型,也就是表達式最外層運算符傳回值的類型,( “最外層”指的是最後執行的運算符)。
比如,下面表達式的類型
就是“+”号運算符所聲明的類型(不管它到底是什麼)。
參數尤其要聲明為某種類型,這一事實涉及一個我提到過但是還未正式讨論的議題:對于每個類型,都有與類型關聯、用于運算該類型值和該類型變量的一系列運算符。也就是說,“運算符op與類型t相關聯”的準确含義是“運算符op具有一個聲明為類型t的參數”。注9比如,整數有常用的算術運算符;日期和時間有特殊的月曆算術運算符;xml檔案有所謂的“xpath”和“xquery”運算符;關系有關系代數運算符;每個類型都有指派(“:=”)和相等比較(“=”)運算符。因而,任何提供正規類型支援(這裡的正規類型支援當然包括允許使用者定義類型)的系統都必須也提供手段, 以便讓使用者可以自定義運算符,因為沒有運算符的類型是沒有用的。注意:你一定會想到,使用者定義運算符可以與系統定義類型相關,也可以與使用者定義類型相關(當然也可以與兩者皆相關)。
現在可以看到,根據定義,某個确定的類型的值和變量隻能使用與該類型相關的運算符進行運算。比如,對于integer這一系統定義類型:
系統提供指派運算符“:=”用于将整數值指派給整型變量。
系統提供了整數字面值的書寫格式。(不過除了簡單字面值之外系統沒有提供其他任何選擇器運算符,也沒有提供任何the_運算符,因為這樣的運算符對于integer這樣的系統定義類型而言并不需要)。
系統為比較整數值提供了比較運算符“=” “≠”“<”等等。
系統為執行針對整數值的算術運算提供了算術運算符“+”“*”等等。
系統沒有為針對整數值的字元串運算提供“||”(連接配接)、substr(子串)- 等字元串運算符;換句話說,系統不支援針對整數的字元串運算。
相反,對于使用者定義類型sno(仍假定它是使用者定義的),我們一定要定義必要的選擇器和the_運算符,而且我們還要定義指派(“:=”)和比較運算符(“=”“≠”,也許還有“<”等)。然而,我們或許不用定義“+” “*”等運算符。這意味着針對供應商編号的算術運算是不被支援的(對兩個供應商編号進行加或乘是什麼含義呢?)。
根據前面說過的内容,現在應該可以明确,在定義一個新類型時至少要包括下述所有工作:
為類型定義名稱(這是很顯然的)。
定義構成類型的值。第8章将詳細讨論此内容。
為類型的值定義隐藏的實體表示。如前所述,這是個實作問題而非模型議題,此書不會進一步讨論。
為選擇或者指定該類型的取值定義一個選擇器運算符。
定義應用于該類型取值和該類型變量的運算符——尤其要包括指派(“:=”)、相等比較(“=”)以及the_運算符(見下文)。
為傳回結果的運算符定義所傳回結果的類型(見下文)。
觀察4~6可知,它們在一起隐含地說明了系統确切知道什麼表達式是合法的,同時也知道這些合法表達式結果的類型。
比如,假設我們有一個使用者定義類型point,表現為兩維空間中的幾何點。那麼,下面給出的就是tutorial d中對于reflect運算符的定義,即對于确定的笛卡爾坐标為(x,y)的點p,傳回笛卡爾坐标為(-x,-y)的“反射”或“相反”點(可以用sql,但是sql中的運算符定義包含大量在此處不研究的細節):
operator reflect ( p point ) returns point ;
return point ( - the_x ( p ) , - the_y ( p ) ) ;
end operator ;
解釋:
第1行顯示了reflect運算符,使用一個參數p,參數類型為point,傳回值類型也為point(是以運算符的聲明類型為point)。
第2行是運算符實作代碼。它由一個單一return語句組成。要傳回的值是調用point選擇器獲得的點;而對point選擇器的調用有兩個實參,對應于要傳回的點的x坐标和y坐标。每個實參均通過the_運算符調用方式進行定義;這些調用生成對應于形參p的實參的x坐标及y坐标,并且對坐标取反來得到期望的結果。注10
第3行标志定義的結束。
雖然本節大部分的讨論都是基于使用者定義類型進行的,但是類似的情況也适用于系統定義類型。唯一的不同在于系統定義類型是由系統而不是使用者提供的。例如,如果integer是一個系統定義類型,那麼它就是由系統來定義名稱、合法整型值、背景的表現形式,以及(如我們所見的)定義對應的文字格式和運算符(“:=” “=” “+”)等等(當然,使用者也可以定義附加的運算符)。
我多次提到過選擇器(selector)運算符,但我卻從未說過(至少沒有明确說過)選擇器(更準确的說,選擇器調用)實際上就是對更為熟知的字面值(literal)概念的概括。注11我想用這個注解說明所有的字面值都是選擇器調用,但并非所有的選擇器調用都是字面值(實際上,隻有選擇器調用中所有的參數都是字面值的情況下,這個選擇器調用才是字面值的)。例如, point(x,y)和point(1.0,2.5)都是選擇器調用,但隻有後者是一個point字面值。它遵循了每個類型有(必須有) 一個關聯的字面值書寫格式的要求。為完整起見,應該加一條:要求每個類型的每個值都必須用某種字面值表示。