爲什麼編程語言會發展出 “類型”?

大家好,我是貓哥。知乎上有一些質量很高的問題,比如這一個:

對於此問題,某百萬大 V 給出了很高的評價:

我曾寫過一篇長文介紹 Python 動態類型 & 強類型的話題(點擊閱讀),兩週前,還從 B 站崩潰的事故分析中,討論了 Lua 弱類型帶來的隱藏 bug 問題(點擊閱讀),更早的時候也分析過爲什麼 Python 沒有 void 類型 。但是,關於爲什麼編程語言會發展出 “類型”、爲什麼變量需要有“類型” 呢?

該問題下有很多大佬的精彩回答,這裏給大家分享一篇,希望對你有所啓發~~


作者:invalid s(Python 貓 · 獲授權轉載)

來源:https://www.zhihu.com/question/425821639/answer/1536947755

因爲…… 我們有一個夢想:如果我們隨便提一下計算機就知道該怎麼做、如果有什麼程序可以自動識別程序裏的邏輯錯誤,那該有多好啊!!!

實踐中,無論你學習數學、物理還是別的什麼,數字都是有類型的。

比如說,桌子兩條邊的夾角是 90 度,這個 90 就是有類型的,爲了方便,我們記爲 90°。

然後,同樣的,桌子的寬度是 90 釐米,這個 90 就是一個長度類型的數據,記爲 90cm。

這兩個 90 是不能直接加的。桌子邊長 90cm,加上桌角 90°,得個 180——什麼鬼東西這是?

類似的,有人調侃小學數學題:紅色的恆星表面溫度是 4000℃,藍色的恆星表面溫度是 9000℃,問它們的溫度加起來等於多少?

問這種題目的都是弱智,對吧。“不同恆星表面溫度加起來” 是沒有意義的。

進一步的,物理學有個手段叫 “量綱分析”,簡單說就是讓數據的單位也參與運算,比如加速度的單位就是(米 / 秒 ²)。

那麼,當你總結出一個物理公式時,就可以先做個 “量綱分析”:公式等號左右兩邊算出來的東西,其量綱一樣嗎?不一樣就丟掉吧,肯定錯了!

類似的,兩個項做加減?它們的量綱一樣嗎?不一樣?你把加速度和速度加起來算什麼鬼。

能通過量綱分析的也未必就對,但通不過量綱分析的肯定是個錯誤。

藉助這個工具,我們可以花費盡量小的代價、儘量快的識別出錯誤,避免浪費更多時間。

類似的,在程序中,我們也可以藉助類型,讓編譯器幫我們查錯。

比如,小張的生日是 7 月 1 日,小王月薪¥12000;程序員腦抽,寫了個計算公式:

int sum = zhang.birthday+wang.salary;

編譯器馬上就可以指出這是個錯誤:

錯誤,+ 兩側數據類型不匹配:datetime 型數據和 decimal 型數據之間不能執行+操作。

不僅如此,我們還可以藉助類型,讓編譯器 “心領神會” 的幫我們選擇(dispatch)正確操作。

比如,我們要給小張記個功,給小王漲 100 塊錢薪水;如果沒有類型系統,那麼你可能得這樣寫:

//zhang.CV指向一塊字符串區域,要往末尾續寫就要計算原有CV的長度
zhang.CV = strcpy(zhang.CV + strlen(zhang.CV)"10月1日獲得個人三等功。");

wang.salary += 100;

很麻煩,對吧。

但如果 zhang.CV 是一個 string 類型,那麼就簡單了:

zhang.CV += "10月1日獲得個人三等功。"; 
wang.salary += 100;

編譯器知道 “字符串的+操作就是往後面拼湊另一個字符串”,和 decimal 的數值增加不是一回事;而我們就可以忘記底層差異,都寫成 += 就行了,編譯器會自動選擇正確實現。

程序寫起來是不是就變得簡潔快捷多了?程序員的記憶負擔是不是也減輕了?

假設你參加單位組織的知識競賽,理論上你可以知無不言言無不盡,可以在試卷上奮筆疾書、充分展示自己的才華……

但現實中呢,答題有時間限制,不可能讓你一個人說一天;試卷留的空白有限,不允許你長篇大論——有些不太專業的人甚至會搞出 “試卷上留白太少寫不完答案” 之類飛機。於是你只能留下一句名言“我知道答案,但是地方太小寫不下”……

計算機也一樣:我們的內存並不是無限的,不可能允許你在 “性別” 裏面填寫“12 歲前男 12 歲後女 18 歲後女變男 20 歲後男變女”……

咳咳,開個玩笑。實踐中,很多時候,一個字節也是生死攸關的。比如你要處理幾千萬條信息,如果每條信息壓縮一個字節,它可能就能放到內存裏完成;但多了一個字節……

Out Of Memory,BOOM!

在正常使用的數據結構裏,人的年齡一般是個 unsigned char,單字節無符號整數最大可以表示到 255,足夠用了;如果你搞錯了:

wang.age = wang.salary

編譯器就會告訴你,“薪水” 使用的類型 decimal 太大,放不到年齡使用的無符號單字節整型裏面(提示你 “操作可能造成數據溢出”)。

不僅如此。

如果完全用二進制表示整數或者簡單定點數,那麼天文數字(比如十後面三四十個零)、高精度小數(精確到小數點 100 位,但前 90 位都是 0),這些是不是也太浪費空間了?當然應該上基於科學計數法的單精度、雙精度數啊。

再比如,二進制僅僅是數字,英文字符、中文方塊字,這些怎麼表示?

因此,我們不得不制定各種各樣的編碼方案;而不同的編碼方案支持的運算法則、具體的運算過程,肯定是不一樣的。

比如,對應到 CPU 整數指令上,就有 “單字節加”“雙字節加”“四字節加”“八字節加” 乃至 “有符號數加” 和“無符號數加”和 “加進位位的加” 和“不加進位位的加”等等區別;甚至 BCD 碼錶示的整數在加法運算後還必須附加一個 DAA 指令完成調整(相應的,減法要用 DAS 指令調整)……

這些,都是半點混淆不得的。

這還僅僅是整數加減法運算。再加上單精度、雙精度浮點數,再加上四則運算以及正切餘切平方求根取對數等等等等……

明白爲什麼現在沒人用匯編編程了吧?

這些東西,都可以藉助類型系統,讓編譯器自動的安排合適指令、確保計算結果正確。

說白了,這還是前面提到的 dispatch(當然,同時還有類型匹配與否的檢查)。

過去,編程語言的類型系統往往僅僅包含 “基本類型” 的完整支持,對用戶自定義數據結構只有有限的支持;面向對象徹底的改變了這一點:從此,就連用戶自定義數據結構,也可以通過 “晚綁定” 在運行時 dispatch 了。

與之同時,面向對象編程也增加了 “接口檢查” 之類動作——你可以調用 Person.Run(),但調用 Stone.Run()編譯器就會報錯。因爲“石頭不會跑”。

這東西繼續發展,還有強類型、弱類型,動態類型、靜態類型,以及 “我不關心你的類型,只關心你能不能執行某個操作” 的鴨類型等等東西。

但歸根結底,類型系統做的就是兩件事:

1、根據類型安排合適的操作

2、藉助類型系統發現部分邏輯錯誤

做爲初學者、業餘開發者,類型系統對你可能是個束縛,因爲你實在搞不明白這東西是幹嘛用的、爲什麼有那麼多類型、這些類型都有哪些差異。那麼 “無” 類型的腳本語言對你來說就非常方便。

但實際上,哪怕號稱 “只有字符串一種類型” 的 TCL 語言也是支持各種數據類型的。只是在你看不見的地方,這種語言自動替你做了類型轉換而已。

這種隱式轉換反而容易形成很多 “坑”。比如知乎上經常被吐槽的 JavaScript,甚至因爲數據值的不同,其加法、判斷等等等操作都會有不同結果——想要用好它,你就不得不死記硬背這些結果,或者寫個表達式就上網查查真僞…… 呸,是上網查查 “加法的正確用法”。這顯然太過累人了。

對專業開發者,如前所述,類型系統是個極爲重要極爲犀利的工具。

用好它,一方面可以藉助它的 “自動分派” 簡化程序,另一方面又可以藉助 “類型檢查” 自動探測出很多很多的錯誤。

你的程序越複雜、規模越大,類型系統的重要性就越突出。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/bRhiM4zrdl5QUJRV7PDlLA