Name before type: why 'age int' is better than 'int age'

Ben Hoyt

名稱在前、型別在後:為什麼『age int』比『int age』更好

原文由 Ben Hoyt 發布,訂閱此部落格

摘要:變數的名稱比型別更重要,因此在宣告時,名稱應該更顯眼、放在前面。

在許多熱門的程式語言中,包括 C、C++、Java 和 C#,當你定義欄位或變數時,會把型別寫在名稱前面。例如(以 C++ 為例):

// Struct definition
struct person {
    std::string name;
    std::string email;
    int age;
};

// Stand-alone variables
std::vector<person> people = get_people();
int min_age = find_minimum_age(people);
std::string domain = parse_email_domain(people[0].email);

在其他語言中,包括 Go、Rust、TypeScript 和 Python(加上型別提示),則是把名稱寫在型別前面。例如(以 Go 為例):

// Struct definition
type Person struct {
    Name  string
    Email string
    Age   int
}

// Stand-alone variables
var people []Person = getPeople()
var minAge int = findMinimumAge(people)
var domain string = parseEmailDomain(people[0].Email)

// Though it's more typical in Go to use := (short variable declarations)
people := getPeople()
minAge := findMinimumAge(people)
domain := parseEmailDomain(people[0].Email)

Go 的 FAQ 裡有個很棒的回答,說明了為什麼 Go 選擇這種順序:「為什麼宣告是反的?」。它開頭就說「只有當你習慣 C 語言,才會覺得它是反的」,這點說得很好——名稱在前的宣告方式在 Pascal 這類語言中有著悠久的歷史。事實上,Go 的型別宣告語法(以及套件)就是直接啟發自 Pascal。

FAQ 接著指出,採用名稱在前的順序,解析會更簡單,而且一次宣告多個變數時,也比在 C 語言中更不容易出錯。在 C 語言中,下面這一行會把 x 宣告為指標,但(乍看之下有點出乎意料!)y 卻是一般的整數:

int* x, y;

而在 Go 中,等價的寫法就會如你所預期的,將兩者都宣告為指標:

var x, y *int

Go 官方部落格甚至有一篇由 Rob Pike 撰寫的深度文章Go 的宣告語法,更詳細地說明了 Go 的語法相較於 C 有哪些優勢,特別是在陣列和函式指標方面。

奇怪的是,這篇文章只稍微暗示了我認為在日常開發中,更重要、也更應該偏好名稱在前的原因:這樣更清晰。

變數的名稱比型別更重要,因此在宣告時,名稱應該更顯眼、放在前面。

我認為這一點可以透過幾種不同的方式輕易證明:

  1. 透過變數宣告中型別推論的普及,這項功能現在大多數語言都有:C# 和 Java 的 var、Go 的 :=、C++ 的 auto 等等。
  2. 透過動態型別語言(如 Python 和 Ruby)的存在,這些語言根本不需要型別——你只有名稱!
  3. 名稱承載了更多的語意。以範例中的 minAge 為例,它清楚地表達了最小年齡這個值,而知道它是 int 並沒有增加太多資訊。當然,型別對型別檢查很有用,但在型別可被推論的情況下,程式碼依然清晰易懂。(英文也有型別推論:你絕不會說「Person Ben Hoyt wrote this text article.」)

我抱持這個觀點已經有一段時間了,但促使我把它寫下來的是最近 Cap’n Proto 1.0 的發布,它讓我想起了 Protocol BuffersCap’n Proto 在 schema 語法上的差異。

在下面哪個範例中,更容易一眼看出欄位名稱呢?這個 Protocol Buffers 的 schema:

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

還是這個 Cap’n Proto 的版本:

struct SearchResponse {
  results @1 :List(Result);
}

struct Result {
  url @1 :Text;
  title @2 :String;
  snippets @3 :List(Text);
}

不知道你怎麼想,但我覺得在 Protocol Buffers 的版本中,最重要的部分(欄位名稱)有點被淹沒了。在 Cap’n Proto 的版本中,由於靠左對齊,欄位名稱則一目了然。

我想重點是:我們無法改變過去,但如果你正在創造一門新語言,請把名稱放在型別前面!

本文章由 muse-spark-1.2-contributor 進行翻譯

留言