編程語言的本質(zhì)是什么?
本文轉(zhuǎn)載自微信公眾號「神說要有光zxg」,作者說要有光zxg。轉(zhuǎn)載本文請聯(lián)系神說要有光zxg公眾號。
作為程序員,我們會接觸到各種各樣的語言:
我們會用 Javascript、Typescript 來寫前端應(yīng)用,用 Java、Go 等來寫后端應(yīng)用,也會用 Python 來寫一些工具腳本。
每種語言都有自己的語法和擅長的領(lǐng)域,那不同的編程語言的區(qū)別是什么呢?編程語言的本質(zhì)是什么呢?
這篇文章我們嘗試探究一下。
從硬件到語言
不同的語言最終都是控制計算機(jī)的一些硬件來工作的,從硬件層面來看他們是沒有區(qū)別的。各種語言只不過描述邏輯的方式、 api 的封裝方式不同而已,底層都跑在同一套硬件上。
那硬件層面做了啥呢?
就拿打印機(jī)來說,它本身是一個機(jī)械的結(jié)構(gòu),但是控制機(jī)械工作的確是通過電子的方式,也就是 0 和 1 的電信號,一般控制它工作的芯片都有不同的引腳,傳高低電平代表不同的含義,那么就可以通過高低電平來控制打印機(jī)做不同的工作。
控制硬件工作的程序就叫做驅(qū)動程序,它把硬件的能力做了封裝,提供了各種 api。這樣,我們就可以通過程序控制各種硬件了。
這些硬件中最特殊的是 CPU,其他硬件提供的指令都是控制該硬件工作的,而 CPU 提供的指令確是可以描述各種邏輯,可以讀寫內(nèi)存,進(jìn)而控制其他硬件。
這樣我們就不用直接控制其他硬件,而是可以通過 CPU 來間接的控制各種硬件,所以,它叫做中央處理器。這些機(jī)器指令叫做指令集,由它所描述的邏輯,就是機(jī)器語言。
硬件是通過電子來控制機(jī)械,提供了驅(qū)動程序,然后又通過 CPU 來實(shí)現(xiàn)各種通用的邏輯,進(jìn)而控制其他硬件。CPU 提供的指令集所描述的邏輯,就叫做機(jī)器語言,這是我們寫的程序最底層的樣子。
為什么要有操作系統(tǒng)
計算機(jī)上肯定不能只跑一個程序,那是最早的計算機(jī),現(xiàn)在的計算機(jī)都是支持多個程序的并發(fā)的。但是不管跑了多少了程序,都是在同一個硬件上跑,只是做了順序、優(yōu)先級等的安排,這種叫做調(diào)度,實(shí)現(xiàn)多個程序的調(diào)度的程序就是操作系統(tǒng)。
既然是為了讓多個程序都能跑在同一個硬件上,那得先給這個跑起來的程序一個名字,就叫做進(jìn)程。進(jìn)程調(diào)度是 CPU 最主要做的事情。進(jìn)程要用各種硬件,那也就還需要內(nèi)存調(diào)度、I/O 調(diào)度等。
總之,操作系統(tǒng)主要做的事情就是支持了程序的并發(fā)執(zhí)行,把各種資源統(tǒng)一管理了起來,實(shí)現(xiàn)了進(jìn)程、內(nèi)存、IO 等等調(diào)度。
同時,為了安全性,操作系統(tǒng)會把程序的運(yùn)行狀態(tài)分為用戶態(tài)和內(nèi)核態(tài),只有內(nèi)核態(tài)可以訪問驅(qū)動,來控制硬件,然后提供了系統(tǒng)調(diào)用給用戶態(tài)來用,因?yàn)槿绻魏纬绦蚨寄茈S意操作硬件,那就不安全了,所以要管控起來。
為什么講編程語言會講到操作系統(tǒng)呢?
因?yàn)槲覀儗懙膽?yīng)用層的代碼都是在操作系統(tǒng)上跑的,用的各種 api 也最終都是操作系統(tǒng)提供的系統(tǒng)調(diào)用來實(shí)現(xiàn)的。
但我們不是直接使用系統(tǒng)調(diào)用,而是用各種語言的標(biāo)準(zhǔn)庫,這些標(biāo)準(zhǔn)庫就是對系統(tǒng)調(diào)用做了進(jìn)一步的封裝,比如創(chuàng)建進(jìn)程、訪問網(wǎng)絡(luò)、訪問內(nèi)存等等。Node.js 的 api、JDK 的 api 都是基于系統(tǒng)調(diào)用封裝的。
操作系統(tǒng)實(shí)現(xiàn)了多個程序在同一套硬件上的并發(fā)執(zhí)行,為了安全,還把程序的運(yùn)行分為了內(nèi)核態(tài)和用戶態(tài),提供了系統(tǒng)調(diào)用來使用操作系統(tǒng)能力,各種語言對系統(tǒng)調(diào)用做了封裝,這樣,我們就能通過這些 API 來控制計算機(jī)了。
編程范式與描述方式
我們講了如何通過機(jī)器語言來控制 CPU 進(jìn)而控制其他硬件,講了操作系統(tǒng)的功能和它提供的系統(tǒng)調(diào)用是怎么被編程語言封裝的,這些都是我們能夠控制計算機(jī)的基礎(chǔ)。
但是我們現(xiàn)在還停留在機(jī)器語言呢,用這個來寫邏輯也太麻煩了,既要考慮怎么表達(dá)邏輯,又要考慮計算機(jī)是怎么執(zhí)行的,比如要訪問那個寄存器、讀寫哪個內(nèi)存等。
能不能簡化一些呢?
首先想到的是把機(jī)器語言做成一些有含義的字符串,叫做匯編語言,這樣描述起來就簡單很多。
但是這依然要考慮表達(dá)的邏輯、計算機(jī)執(zhí)行細(xì)節(jié)這兩個方面,而執(zhí)行細(xì)節(jié)其實(shí)與邏輯沒關(guān)系,而且不同機(jī)器和操作系統(tǒng)的執(zhí)行細(xì)節(jié)也可能不同。
能不能我只管怎么表達(dá)邏輯,然后通過一種方式來轉(zhuǎn)成帶有執(zhí)行細(xì)節(jié)的機(jī)器語言呢?
這種就是高級語言了,它的特點(diǎn)就是沒有具體的執(zhí)行細(xì)節(jié),只關(guān)心邏輯的表達(dá),實(shí)現(xiàn)這種轉(zhuǎn)換的就是編譯器。(當(dāng)然,也可以做成一個解釋執(zhí)行其他程序的中間程序,叫做解釋器)
而描述邏輯這件事情有不同的方式,比如我可以通過一個個函數(shù)來組織邏輯,把數(shù)學(xué)那套思維拿過來,這叫函數(shù)式,也可以通過一個個對象來組織邏輯,這叫做面向?qū)ο?,這些不同的描述邏輯的思路就是編程范式。
編程語言主要就是實(shí)現(xiàn)了某幾種編程范式,這樣,程序員就可以通過不同的方式來描述邏輯,由編譯器去轉(zhuǎn)成帶有計算機(jī)執(zhí)行細(xì)節(jié)的機(jī)器代碼。
不同語言實(shí)現(xiàn)的編程范式不同,也就是描述邏輯的方式不同,這是語言之間最大的區(qū)別。 至于能做什么,這個不是區(qū)別,只要對系統(tǒng)調(diào)用封裝一下,做成一些庫就可以支持。
比如 Javascript 最開始只可以在瀏覽器上跑,描述渲染邏輯,但后來有了 Node.js 后,它同樣可以用來描述一些腳本或者服務(wù)端邏輯。
像現(xiàn)在的跨端引擎,不就是對操作系統(tǒng)能力做了封裝,通過 Js 來描述邏輯,然后由 native 來調(diào)用操作系統(tǒng)能力么?
還有 electron、hybrid 等等,這些都是 Javascript 的 runtime,他們擴(kuò)展的是 api,并沒有擴(kuò)展 js 語言本身。
那什么擴(kuò)展了 Javascript 語言本身呢?是 Typescript 這種編譯成 Javascript 的語言,它提供了類型系統(tǒng),可以靜態(tài)檢查出程序中的一些錯誤。
為了分離邏輯的表達(dá)和程序執(zhí)行的細(xì)節(jié),我們實(shí)現(xiàn)了高級語言,這樣程序員只需要專注邏輯的表達(dá),然后通過編譯器/解釋器來轉(zhuǎn)換成帶有執(zhí)行細(xì)節(jié)的機(jī)器語言代碼。而邏輯表達(dá)有不同的方式,比如面向?qū)ο?、函?shù)式等,每種編程語言會實(shí)現(xiàn)其中的幾種,這是語言之間最大的區(qū)別。語言只是表達(dá)邏輯用的,至于能做什么,則是 api的事情,只要對系統(tǒng)能力做下封裝,就可以擴(kuò)展其他的 api,進(jìn)而可以寫該領(lǐng)域的邏輯,比如 Node.js、Electron、跨端引擎等都是 api 的擴(kuò)展。
總結(jié)
我們從硬件、操作系統(tǒng)、編程范式三個層次來探討了編程語言的本質(zhì):
硬件是用電子控制機(jī)械,通過驅(qū)動程序來驅(qū)動硬件工作,而 CPU 可以描述通用的邏輯,進(jìn)而控制其他硬件,我們就是通過控制 CPU 來間接控制各種硬件的,所以它叫做中央處理器。它提供的指令集所表達(dá)的邏輯叫做機(jī)器語言。
操作系統(tǒng)實(shí)現(xiàn)了程序的并發(fā)執(zhí)行,讓一套硬件上可以同時跑多個程序,叫做進(jìn)程。操作系統(tǒng)支持了進(jìn)程、內(nèi)存、IO 等各種調(diào)度。為了安全,把程序的執(zhí)行分成了用戶態(tài)和內(nèi)核態(tài)兩個狀態(tài),內(nèi)核態(tài)才可以通過驅(qū)動控制硬件,然后把它做成了系統(tǒng)調(diào)暴露給用戶態(tài)。各種語言的標(biāo)準(zhǔn)庫就是通過系統(tǒng)調(diào)用來使用操作系統(tǒng)的能力的。
機(jī)器語言是我們控制計算機(jī)最根本的方式,但是它要邏輯的表達(dá)和計算機(jī)執(zhí)行細(xì)節(jié)兩方面,為了分離兩者,我們做了編譯器/解釋器來完成這種轉(zhuǎn)換,這樣我們只需要表達(dá)邏輯即可,這叫做高級語言。描述邏輯有不同的方式,叫做編程范式,每種編程語言都實(shí)現(xiàn)了某幾種編程范式。不同編程語言的區(qū)別只是表達(dá)邏輯的方式不同,至于可用的 api,這個可以通過庫或者 runtime 來擴(kuò)展。
所以,如果讓你做一門編程語言,你要做什么呢?
你要先選擇一種編程范式,用它來表達(dá)邏輯,然后要設(shè)計細(xì)節(jié)的語法。
之后實(shí)現(xiàn)編譯器/解釋器來讓它能夠轉(zhuǎn)成控制計算機(jī)運(yùn)行的機(jī)器語言。
之后要實(shí)現(xiàn)對操作系統(tǒng)能力做了封裝的標(biāo)準(zhǔn)庫。
然后如果你要表達(dá)不同領(lǐng)域的邏輯,則要實(shí)現(xiàn)不同領(lǐng)域的一些庫,比如圖形領(lǐng)域的、桌面端的、web 服務(wù)器的等等。
這是實(shí)現(xiàn)編程語言的思路,也是我們理解編程語言的思路。