第一堂資訊課時,電腦老師總會(應該吧)說「C++ 是靜態 & 弱類型語言、Python 是動態 & 強類型語言」。
但 C++ 又哪裏「靜態」?Python 又為何是「強類型」?
這篇文章,我想談談「動態/靜態類型」、「強/弱」語言之間的差異。
所有語言皆可分成兩種:動態類型(Dynamic Typed)語言 和 靜態類型(Statically Typed)語言。
x = 1
# 這時候 x 的 type 是 int,
print(1) # 1
x = "hello"
# 這時候 x 的 type 是 str
print(x) # x
在動態類型語言中,一個變數的 type 可以反覆更改。在上面 Python 的範例中,x 一開始的 type 是 int。經過更改後,type 則變成 str。
動態類型語言有 Python、JavaScript、Ruby、Lua、PHP、Perl、Racket/Scheme 等。
幾乎所有的動態語言都經由直譯執行,只有少數語言(例如 Common Lisp 和 Racket)可以直接編譯成執行檔。
因爲不必手動標記類型 & 確定變數類型,所以很容易糊出一個程式的原型。不少公司的產品最初都由動態語言編寫,而後才轉到靜態語言。
Facebook 最初是由 PHP 寫成,後來才陸續有了 C++、Java 等其他語言。
因爲對 function 的參數沒有類型要求,所以動態語言容易實現「泛型」。
def add(a, b):
return a + b
int add(int a, int b) {
return a + b
}
double add(double a, double b) {
return a + b
}
int main() {
int n = 2;
n = "String" // 報錯,n 不能賦值爲 "String"
}
相較之下,靜態語言的變數類型在初始化時便決定,之後也無法再度更改。
早期的靜態語言(C、C++、Java)必須手動寫出類型標記(範例中的 int),但近二十年出現的靜態語言(C#、Kotlin、Rust、Swift)都支持類型推導(Type Inference),編譯器會幫你判斷變數的類型。
比如說在 Rust 能這樣寫:
fn main() {
let x = 20; // Rust 知道 x 的類型是 i32
println!("{}", x);
}
常見的靜態語言有 C/C++、Java、C#、Rust、Swift、Haskell、OCaml、Kotlin、Golang 等。
大部分的靜態語言都採用編譯執行,不過也有一些語言(如 OCaml)可以直譯執行。
因爲變數的類型無法更改,所以編譯器能提前找出可能的類型錯誤。
因此大部分大公司的大 project 都由靜態語言寫成。
因爲靜態語言容易編譯成執行檔,速度較多數採直譯的動態語言快。
因爲編譯器可以由程式中推出變數的類型、一個 class 有哪些函數,所以普遍而言 IDE 對靜態語言的支持較好。
不過這點在 Microsoft 搞出 LSP 後差距減少不少。