通俗解釋一下“強靜態類型”

我寫軟件已經有 20 多年了,隨著時間的推移,我越來越確信強靜態類型不僅是一個好主意,而且幾乎總是正確的選擇。

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

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

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

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

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

1、類型導致更少的錯誤

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

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

// 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}!`;}

復制代碼

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

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

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

// 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 輸入(因此始終是字符串)收集的用戶輸入。這將導致:

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

復制代碼

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

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

2、類型帶來更好的開發體驗

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

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

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

  • 他們可以遵循類型定義來了解事物的使用位置。

  • 修改東西要容易得多,因為更改會觸發編譯錯誤。

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

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 作為參數。其中一些問題可以通過詳盡的文檔來解決,但是:(1)如果使用類型可以實現更多的功能,為什么還要費心呢?(2)文檔過時,這里的代碼是 document。

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

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

復制代碼

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

3、我們對類型系統中的所有內容進行編碼

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

例如,Redis 是一個基于字符串的協議,沒有固有的類型。我們使用 Redis 進行緩存(以及其他功能)。問題是,我們所有的優秀的類型優勢將在 Redis 層丟失,并且可能發生 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 做了兩件事。首先,我們要求鍵是某種類型的(不是泛型字符串),要創建這種類型,需要調用一個特定的函數。我們做的第二件事,是將鍵與值強制配對。

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

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 linelet pet: Pet = cache.get(PersonCacheKey::new(id));

復制代碼

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

請考慮以下函數:

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

復制代碼

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

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

復制代碼

第二,我們正在失去可發現性。很難知道寵物與人有關系。

因此,在 Svix,我們為每個類型都有一個特殊的類型,以確保沒有錯誤。調整后的代碼如下所示:

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、那么為什么不是每個人都喜歡類型呢?

反對類型一方論證的主要依據是:

  • 開發速度

  • 學習曲線和類型復雜性

  • 所需的工作量和樣板

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

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

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

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

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

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

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

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

是的,在不支持推理的語言中,類型可能是一種痛苦,例如 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);

復制代碼

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

5、結語

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

6、快速開發工具推薦

這是一款基于 SpringBoot+Vue 的前后端分離的項目,麻雀雖小,五臟俱全,開箱即用!

JNPF 開發平臺的前端采用 Vue.js,這是一種流行的前端 JavaScript 框架,用于構建用戶界面。Vue.js 具有輕量級、可擴展性強和生態系統豐富等特點,被廣泛應用于構建單頁面應用程序。

后端采用 SpringBoot,這是一種基于 Java 的開源框架,用于簡化 Spring 應用的初始搭建以及開發過程。SpringBoot 通過自動配置和約定大于配置的原則,簡化了 Spring 應用的配置和開發。此外,JNPF 還采用 MyBatis-Plus 作為持久層框架,它是一個功能強大的 MyBatis 擴展,可以大大簡化數據庫操作的開發。

核心功能:表單引擎、可視化引擎、BI 引擎、流程引擎、權限引擎、門戶引擎、大屏引擎、接口中心、物聯平臺。嘗試自己開發一個應用!應用地址:https://www.jnpfsoft.com/?csdnxue

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/210154.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/210154.shtml
英文地址,請注明出處:http://en.pswp.cn/news/210154.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

有什么進銷存軟件能對接微信小程序?

有什么進銷存軟件能對接微信小程序&#xff1f; 據我所知&#xff0c;很多進銷存軟件都有配套的微信小程序吧。 以我們現在用的這個為例&#xff0c;這也是同行推薦過來的&#xff0c;很好用&#xff0c;而且性價比很高—— 在線平臺&#xff0c;無需下載APP&#xff0c;搭載…

C語言實戰演練之跳動的愛心C語言版

跳 動 的 愛 心 - LOVE - 完整程序 #include <stdio.h> #include <math.h> #include <windows.h> #include <tchar.h> float f(float x, float y, float z) {float a x * x 9.0f / 4.0f * y * y z * z - 1;return a * a * a - x * x * z * z …

ELK簡單介紹一

任務背景 運維人員需要對系統和業務日志進行精準把控&#xff0c;便于分析系統和業務狀態。日志分布在不同的服務器上&#xff0c;傳統的使用傳統的方法依次登錄每臺服務器查看日志&#xff0c;既繁瑣又效率低下。所以我們需要集中化的日志管理工具將位于不同服務器上的日志收…

藍橋杯日期問題

藍橋杯其他真題點這里&#x1f448; 注意日期合法的判斷 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main{static int[] days {0,31,28,31,30,31,30,31,31,30,31,30,31};static BufferedReader in new Buf…

【核心重點】Flink四大基石

1. Time&#xff08;時間機制&#xff09; 時間概念 處理時間&#xff1a;執行具體操作時的機器時間&#xff08;例如 Java的 System.currentTimeMillis()) &#xff09;事件時間&#xff1a;數據本身攜帶的時間&#xff0c;事件產生時的時間。攝入時間&#xff1a;數據進入 …

linux vim 基礎設置-自動填充文件頭

前言 當前為vimrc腳本設置&#xff0c;腳本位置在 ~/.vimrc or /etc/vimrc 當前為首次打開 C C Shell 文件&#xff0c;自動填充對應文件頭信息&#xff0c;再次打開時會修改對應時間信息 :set nu "顯示行號 :set hlsearch "搜索時 高亮"新建 .c .cpp .sh文件&a…

理解Go語言中的defer

引言 Go有許多在其他編程語言中可以找到的常見控制流關鍵字,例如if、switch、for等。defer是其他大多數編程語言中沒有的關鍵字,盡管它不太常見,但你很快就會看到它在你的程序中有多么有用。 defer語句的主要用途之一是清理資源,例如打開的文件,網絡連接和數據庫句柄。在…

在AWS Lambda上部署EC2編譯的FFmpeg工具——自定義層的方案

大綱 1 確定Lambda運行時環境1.1 Lambda系統、鏡像、內核版本1.2 運行時1.2.1 Python1.2.2 Java 2 環境準備2.1 創建EC2實例 3 編譯FFmpeg3.1 連接EC2 4 編譯5 上傳S3存儲桶5.1 創建S3桶5.2 創建IAM策略5.3 創建IAM角色5.4 EC2關聯角色5.5 修改桶策略5.6 打包并上傳 6 創建Lamb…

智能優化算法應用:基于海鷗算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用&#xff1a;基于海鷗算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用&#xff1a;基于海鷗算法3D無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.海鷗算法4.實驗參數設定5.算法結果6.參考文獻7.MA…

【nuxt3】cannot read preperties of null (reading ‘$nuxt‘)

問題描述 vue3 中&#xff0c;通過 createVNode 創建子組件實例時&#xff0c;發現子組件無法獲取到父組件中的 router、store 信息&#xff0c;一旦子組件使用就會報錯。 問題原因 通過控制臺斷點調試&#xff0c;發現時 appContext 值為空導致的。懷疑是創建子組件的時候&a…

海外地區開啟IPV6無法訪問服務器問題

前言 最近有海外地區的用戶反饋無法訪問公司的網絡&#xff0c;無法下載應用和系統進行升級。了解到瀏覽器可以正常訪問公司域名&#xff0c;谷歌&#xff0c;油管等都能正常使用。日志分析GET請求服務器數據時沒有得到應答&#xff0c;最終查詢網絡相關修改確認與網絡IPV6有關…

掌握游戲開發的全方位知識:這些內容你一定要知道

游戲開發是一項涉及多學科的綜合性工作&#xff0c;從游戲設計到編程、美術、音頻、測試等多個方面都需要開發者具備廣泛的知識。以下是進行游戲開發時需要掌握的主要知識領域。 首先&#xff0c;游戲設計是整個過程的基石。這包括游戲機制和玩法設計、關卡設計、用戶界面&…

表示你的shell未被正確配置以使用conda activate--換成清華源anaconda

1 CommandNotFoundError: Your shell has not been properly configured to use conda activate. If using conda activate from a batch script, change your invocation to CALL conda.bat activate.To initialize your shell, run$ conda init <SHELL_NAME>這個錯誤提…

uniapp-獲取手機型號

要獲取當前設備的手機型號&#xff0c;您可以使用uni-app提供的uni.getSystemInfo() API來實現此目的。 代碼示例&#xff1a; uni.getSystemInfo({success: function(res) {console.log("手機型號&#xff1a;" res.platform)} })該方法會返回一個包含設備信息的…

JFrog推出面向Hugging Face的原生集成,為 ML 模型提供強大支持,實現DevOps、安全和AI的協調統一

2023年12月5日 —— 流式軟件公司、企業軟件供應鏈平臺提供商JFrog推出ML模型管理功能&#xff0c;這是業界首套旨在簡化機器學習&#xff08;ML&#xff09;模型管理和安全性的功能。JFrog 平臺中的全新ML模型管理功能使AI交付與企業現有的 DevOps 和 DevSecOps 實踐保持一致&…

計算機評價的主要性能指標

對計算機評價的主要性能指標如下&#xff1a; 1&#xff0e;時鐘頻率&#xff08;主頻&#xff09; 主頻是計算機的主要性能指標之一&#xff0c;在很大程度上決定了計算機的運算速度。CPU 的工作節拍是由主時鐘來控制的&#xff0c;主時鐘不斷產生固定頻率的時鐘脈沖&#xff…

一個簡單的可視化的A星自動尋路

一個簡單的應用場景&#xff0c;流程圖連線 源碼&#xff1a; addExample("A星路徑查找", function () {return {template: <div><div ref"main"></div></div>,data() { return {}; },computed: {},methods: {},mounted() {var c…

Python中的比較兩個字符串

更多資料獲取 &#x1f4da; 個人網站&#xff1a;ipengtao.com 在Python編程中&#xff0c;字符串比較是一項常見且關鍵的操作&#xff0c;涵蓋了諸多方法和技巧。比較兩個字符串是否相等、大小寫是否一致&#xff0c;或者在一個字符串中尋找特定的子字符串&#xff0c;都是日…

征途漫漫:汽車MCU的國產替代往事

01.西雁東飛&#xff0c;南下創業 1985年&#xff0c;山東大學物理系畢業的周生明加入878廠&#xff08;“北霸天”&#xff09;參與MOS電路研發&#xff0c;隨后幾年&#xff0c;大洋彼岸的英特爾相繼推出CPU 386\486、奔騰系列等產品。在摩爾定律的凸顯、進口和走私的劇烈沖…

基于Java房屋租賃管理系統

基于Java房屋租賃管理系統 功能需求 1、房源信息管理&#xff1a;系統需要能夠記錄和管理所有房源的詳細信息&#xff0c;包括房屋地址、房屋面積、租金、付款方式、房屋類型等。管理員應該可以添加、編輯和刪除房源信息。 2、租戶信息管理&#xff1a;系統需要能夠記錄和管…