自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

強靜態(tài)類型,真的無敵

譯文 精選
開發(fā) 前端
我可以在許多話題上看到雙方的爭論,比如vs.,制表符vs.空格,甚至更具爭議性的主題。盡管在這種情況下,與收益相比,成本是如此之低,以至于我不明白為什么有人會選擇不使用類型。我不知道自己忽略了什么,我只知道,強類型是我愿意死在上面的一座山。

作者丨Tom Hacohen

編譯丨千山

我寫軟件已經(jīng)有20多年了,隨著時間的推移,我越來越確信強靜態(tài)類型不僅是一個好主意,而且?guī)缀蹩偸钦_的選擇。

非類型化語言(或語言變體)肯定有用途,例如,當使用REPL時,或者在已經(jīng)無可救藥的無類型環(huán)境(例如shell)中使用一次性腳本時,它們會更好。然而,在幾乎所有其他情況下,強類型都是首選。

不使用類型是有好處的,比如更快的開發(fā)速度,但與所有的好處相比,它們就顯得微不足道了。對此,我要說:

編寫沒有類型的軟件可以讓你全速前進——全速沖向懸崖。

關(guān)于強靜態(tài)類型的問題很簡單:你是愿意多做一點工作,在編譯時檢查不變量(或非編譯語言的類型檢查時間),還是愿意少做一點工作,在運行時強制執(zhí)行它們,或者更糟糕的是,即使在運行時也不強制執(zhí)行(JavaScript,我在看著你…)。

在運行時出錯是一個糟糕的想法。首先,這意味著在開發(fā)過程中你不會總是抓住它們。其次,當你抓住他們的時候,它會以面向客戶的方式發(fā)生。是的,測試有幫助,但是考慮到無限的可能性,為每一個可能的錯誤類型函數(shù)參數(shù)編寫測試是不可能的。即使可以,擁有類型也比測試錯誤類型容易得多。

1、類型導(dǎo)致更少的錯誤

類型還為代碼提供注釋,使人類和機器都受益。擁有類型是一種更嚴格地定義不同代碼段之間協(xié)定的方法。

請考慮以下四個示例。它們都做完全相同的事情,只是契約定義級別不同。

// Params: Name (a string) and age (a number).
function birthdayGreeting1(...params) {
    return `${params[0]} is ${params[1]}!`;
}

// Params: Name (a string) and age (a number).
function birthdayGreeting2(name, age) {
    return `${name} is ${age}!`;
}

function birthdayGreeting3(name: string, age: number): string {
    return `${name} is ${age}!`;
}

第一個甚至沒有定義參數(shù)的數(shù)量,因此如果不閱讀文檔,很難知道它的作用。我相信大多數(shù)人都會同意第一個是令人討厭的,不會寫這樣的代碼。雖然它的思想與類型非常相似,但它是關(guān)于定義調(diào)用者和被調(diào)用者之間的契約。

至于第二個和第三個,由于類型的原因,第三個將需要更少的文檔。代碼更簡單,但不可否認,優(yōu)點相當有限。好吧,直到你真正更改這個函數(shù)前......

在第二個和第三個函數(shù)中,作者假設(shè)年齡是一個數(shù)字。因此,更改代碼絕對沒問題,如下所示:

// Params: Name (a string) and age (a number).
function birthdayGreeting2(name, age) {
    return `${name} will turn ${age + 1} next year!`;
}

function birthdayGreeting3(name: string, age: number): string {
    return `${name} will turn ${age + 1} next year!`;
}

問題是使用此代碼的某些位置接受從HTML輸入(因此始終是字符串)收集的用戶輸入。這將導(dǎo)致:

> birthdayGreeting2("John", "20")
"John will turn 201 next year!"

雖然類型化版本將無法正確編譯,因為此函數(shù)將年齡除外,否則年齡是數(shù)字,而不是字符串。

在調(diào)用方和被調(diào)用方之間建立協(xié)定對于代碼庫非常重要,這樣調(diào)用方就可以知道被調(diào)用方何時更改。這對于開源庫尤其重要,因為調(diào)用方和被調(diào)用方不是由同一組人編寫的。沒有這個合同,就不可能知道事情在發(fā)生時是如何變化的。

2、類型帶來更好的開發(fā)體驗

IDE和其他開發(fā)工具也可以使用類型來極大地改善開發(fā)體驗。如果你的任何期望是錯誤的,你將在編寫代碼時得到通知。這大大降低了認知負荷。你不再需要記住上下文中所有變量和函數(shù)的類型。編譯器將與你同在,并在出現(xiàn)問題時告訴你。

這也帶來了一個非常好的額外好處:更容易重構(gòu)。你可以相信編譯器會讓你知道你所做的更改(例如上面示例中的更改)是否會破壞代碼中其他地方所做的假設(shè)。

類型還可以使新工程師更容易加入代碼庫或庫:

  • 他們可以遵循類型定義來了解事物的使用位置。
  • 修改東西要容易得多,因為更改會觸發(fā)編譯錯誤。

讓我們考慮對上述代碼進行以下更改:

class Person {
  name: string;
  age: number;
}

function birthdayGreeting2(person) {
    return `${person.name} will turn ${person.age + 1} next year!`;
}

function birthdayGreeting3(person: Person): string {
    return `${person.name} will turn ${person.age + 1} next year!`;
}

function main() {
  const person: Person = { name: "Hello", age: 12 };

  birthdayGreeting2(person);

  birthdayGreeting3(person);
}

很容易查看(或使用IDE查找)所有使用過的位置。你可以看到它被啟動,你可以看到它被使用。然而,為了知道它的用途,你需要閱讀整個代碼庫。

這樣做的另一方面是,在看的時候,很難知道它期望a作為參數(shù)。其中一些問題可以通過詳盡的文檔來解決,但是:(1)如果使用類型可以實現(xiàn)更多的功能,為什么還要費心呢?(2)文檔過時,這里的代碼是document。

這與你不編寫代碼的方式非常相似:

// a is a person
function birthdayGreeting2(a) {
    b = a.name;
    c = a.age;
    return `$ will turn ${c + 1} next year!`;
}

你可能希望使用有用的變量名。類型也是一樣的,它只是steriods上的變量名。

3、我們對類型系統(tǒng)中的所有內(nèi)容進行編碼

在Svix,我們喜歡類型。事實上,我們嘗試在類型系統(tǒng)中對盡可能多的信息進行編碼,以便在編譯時捕獲所有可以在編譯時捕獲的錯誤;同時也要壓縮開發(fā)者體驗改進的額外里程。

例如,Redis是一個基于字符串的協(xié)議,沒有固有的類型。我們使用Redis進行緩存(以及其他功能)。問題是,我們所有的優(yōu)秀的類型優(yōu)勢將在Redis層丟失,并且可能發(fā)生bug。

考慮下面這段代碼:

pub struct Person {
    pub id: String,
    pub name: String,
    pub age: u16,
}

pub struct Pet {
    pub id: String,
    pub owner: String,
}


let id = "p123";
let person = Person::new("John", 20);
cache.set(format!("person-{id}"), person);
// ...
let pet: Pet = cache.get(format!("preson-{id}"));

代碼片段中有幾個bug:

  • 第二個鍵名稱有個拼寫錯誤。
  • 我們正在嘗試將一個人裝入寵物類型。

為了避免這樣的問題,我們在Svix做了兩件事。首先,我們要求鍵是某種類型的(不是泛型字符串),要創(chuàng)建這種類型,需要調(diào)用一個特定的函數(shù)。我們做的第二件事,是將鍵與值強制配對。

所以上面的例子看起來像這樣:

pub struct PersonCacheKey(String);

impl PersonCacheKey {
    fn new(id: &str) -> Self { ... }
}

pub struct Person {
    pub id: String,
    pub name: String,
    pub age: u16,
}

pub struct PetCacheKey;

pub struct Pet {
    pub id: String,
    pub owner: String,
}


let id = "p123";
let person = Person::new(id, "John", 20);
cache.set(PersonCacheKey::new(id), person);
// ...
// Compilation will fail on the next line
let pet: Pet = cache.get(PersonCacheKey::new(id));

這已經(jīng)好多了,并且不可能出現(xiàn)前面提到的任何錯誤。雖然我們可以做得更好!

請考慮以下函數(shù):

pub fn do_something(id: String) {
    let person: Person = cache.get(PersonCacheKey::new(id));
    // ...
}

它有幾個問題。首先是不太清楚id應(yīng)該用來做什么。是一個人嗎?一個寵物嗎?很容易意外地用錯誤的名稱調(diào)用它,就像下面的例子一樣

let pet = ...;
do_something(pet.id); // <-- should be pet.owner!

第二,我們正在失去可發(fā)現(xiàn)性。很難知道寵物與人有關(guān)系。

因此,在Svix,我們?yōu)槊總€類型都有一個特殊的類型,以確保沒有錯誤。調(diào)整后的代碼如下所示:

pub struct PersonId(String);
pub struct PetId(String);

pub struct Person {
    pub id: PersonId,
    pub name: String,
    pub age: u16,
}

pub struct Pet {
    pub id: PetId,
    pub owner: PersonId,
}

這確實比我們之前的例子要好得多。

4、那么為什么不是每個人都喜歡類型呢?

反對類型一方論證的主要依據(jù)是:

  • 開發(fā)速度
  • 學(xué)習(xí)曲線和類型復(fù)雜性
  • 所需的工作量和樣板

首先,我認為即使上述所有情況都是真的,上面提到的優(yōu)勢也值得麻煩。

首先是開發(fā)速度。沒有類型的原型設(shè)計肯定要快得多。你可以注釋掉代碼片段,并且不會讓編譯器向你抱怨。你可以為某些字段設(shè)置錯誤的值,直到你準備好找出正確的字段等。

雖然就像我上面說的:“編寫沒有類型的軟件可以讓你全速前進。全速向懸崖走去。”問題在于,這只是激進且不必要的技術(shù)債務(wù)。當你需要調(diào)試代碼無法正常工作的原因時(無論在本地、測試套件或生產(chǎn)環(huán)境中),你都需要多次支付這筆費用。

至于學(xué)習(xí)曲線:是的,學(xué)習(xí)更多的東西需要時間。不過我得說,大多數(shù)人不需要成為類型專家。他們可以使用非常簡單的類型表達式過日子,并詢問他們是否曾經(jīng)遇到瓶頸。然而,如果你讓事情保持簡單,你可能很少會碰到一個。

此外,人們已經(jīng)被要求學(xué)習(xí)如何編碼,學(xué)習(xí)框架(React,Axum等),以及許多其他東西。我認為學(xué)習(xí)負擔并不像人們想象的那么重。

最后,但并非最不重要的是,關(guān)于學(xué)習(xí)曲線:我堅信,不必了解類型而減少學(xué)習(xí)曲線的好處遠遠小于在特定代碼庫上使用類型腳本的好處。特別是因為學(xué)習(xí)類型是一次性的成本。

最后一點是關(guān)于在代碼庫中使用類型所需的工作量和樣板。我堅信,比起不寫類型所需要的工作量,這種工作量實際上要少得多。

不使用類型需要大量的文檔和測試,才能達到基本的健康水平。文檔可能會過時,測試也會過時;無論哪種方式,它們都比添加正確的類型需要更多的努力。閱讀帶有類型的代碼也更容易,因為你可以內(nèi)聯(lián)獲取類型,而不是在函數(shù)文檔中獲取類型,在函數(shù)文檔中,它的格式不一致,并且增加了很多干擾。

是的,在不支持推理的語言中,類型可能是一種痛苦,例如Java可能很乏味:

Person person1 = newPerson();
Person person2 = newPerson();
Person child = makeChild(person1, person2);

而其他具有推理功能的語言(如 Rust)則要好得多:

let person1 = new_person();
let person2 = new_person();
let child = make_child(person1, person2);

因此,擁有合適的工具肯定會有所幫助。說到工具,為了獲得類型的好處,你可能需要使用支持語言感知的現(xiàn)代代碼完成的代碼編輯器(或 IDE)。

5、結(jié)語

我可以在許多話題上看到雙方的爭論,比如vs.,制表符vs.空格,甚至更具爭議性的主題。盡管在這種情況下,與收益相比,成本是如此之低,以至于我不明白為什么有人會選擇不使用類型。我不知道自己忽略了什么,我只知道,強類型是我愿意死在上面的一座山。

參考鏈接:https://www.svix.com/blog/strong-typing-hill-to-die-on/

責任編輯:武曉燕 來源: 51CTO技術(shù)棧
相關(guān)推薦

2009-09-08 14:56:55

強類型DataContLinq to SQL

2019-10-08 11:09:33

網(wǎng)絡(luò)5GWi-Fi

2010-09-26 09:01:18

SQL強類型查詢

2009-08-04 17:52:25

ActorLite強類型

2011-04-13 08:49:33

DataSet強類型化

2020-08-31 19:17:24

Python強類型語言弱類型語言

2021-04-21 07:53:14

云原生PulsarGo

2017-11-20 10:21:17

量子點顯示器OLED

2024-07-09 08:00:48

C#var?關(guān)鍵字

2012-07-02 10:43:49

JVMGroovyJava

2023-10-29 16:18:26

Go接口

2021-07-21 09:15:57

Python工具編程語言

2012-07-12 11:23:07

GroovyJVM

2014-04-01 15:25:18

2019-06-04 09:30:30

PythonSQL數(shù)據(jù)庫

2009-06-11 17:54:00

Visual StudDataSet

2011-06-07 18:34:13

SEO

2023-10-23 06:47:37

Redis磁盤存儲

2025-04-09 11:00:00

NAT網(wǎng)絡(luò)網(wǎng)絡(luò)地址轉(zhuǎn)換

2010-01-18 09:25:33

ASP.NET MVC
點贊
收藏

51CTO技術(shù)棧公眾號