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

聊聊反射和元編程,你學(xué)會了嗎?

開發(fā) 前端
在 Go 中進行類型斷言(type assertion)時,從某種程度上來說,這也是一種反射,對嗎?在運行時你會說“這是某種類型,但我們不知道具體是什么類型,所以我要斷言它是某個特定類型,如果斷言成功,我就可以執(zhí)行某些操作?!?/div>

Mat Ryer: 大家好,歡迎來到 Go Time。今天我們將討論反射(reflection),以及它在 Go 中的含義。我們會討論 reflect 包,它能做什么,以及一些非常有趣的使用案例,甚至包括標(biāo)準庫中的一些例子。最后,我們還會對它發(fā)表一些主觀看法,毫無疑問。

今天加入我們的是 Jaana B. Dogan。你好,Jaana。

Jaana Dogan: 你好!

Mat Ryer: 歡迎回來。你最近怎么樣?

Jaana Dogan: 很好!你呢?

Mat Ryer: 嗯,還不錯,謝謝。Jaana,如果你不介意我說的話,你聽起來好像不太興奮……不過別擔(dān)心,這會讓你開心起來的。Jon Calhoun 也在這里。你好,Jon!

Jon Calhoun: 嗨,Mat。不,她看起來是在思考什么。其他人看不到我們的視頻,但她看起來是在深思。

Mat Ryer: 對啊,她在“反思”。

Jon Calhoun: 是的,她在“反思”。

Jaana Dogan: 我會告訴你們我在做什么……確實如此。

Mat Ryer: 好吧,那我們從頭開始吧。為了那些不熟悉的人,什么是反射?reflect 包能為我們提供什么功能?

Jon Calhoun: 從高層次來看,它有點像元編程(meta-programming),或者說是運行時與代碼交互。我是這么理解的,雖然我不知道官方定義是什么,但我見過的所有使用它的例子都是:當(dāng)你的代碼正在運行時,你想要檢查其他代碼片段,或者查看其他內(nèi)容,找到一些關(guān)于它們的信息,或者嘗試修改它們的不同方面……所以這涉及的內(nèi)容不是在開發(fā)時由開發(fā)者完成的,而是在程序運行時進行的。

Mat Ryer: 是的,動態(tài)語言經(jīng)常這樣做,對吧?比如 Ruby,還有 JavaScript。我想在 JavaScript 中,你可以在運行時將方法添加到字符串中,幾乎可以做任何你想做的事情。它是一種非常靈活的語言。而 Go 是一種強類型語言,它故意不允許這樣做,但 reflect 包是一個例外。

Jon Calhoun: 正如你所說的,動態(tài)語言中幾乎不會將這種行為視為某種特別的東西……它就是語言的一部分。這是人們自然會做的事情。如果你曾經(jīng)使用過 Ruby 或類似的語言,它顯得非常自然,因為你看到大家都在這樣做。在任何代碼庫中,這都不會顯得特別突出。但是在 Go 語言中,不僅你需要顯式導(dǎo)入 reflect 包,功能也非常有限。我認為這是有意為之的,并且與 Go 想要實現(xiàn)的目標(biāo)一致。

Jaana Dogan: 我來自強類型背景,我本來想說反射是類型系統(tǒng)無法作為一等公民提供的所有功能……但后來我看了一下維基百科頁面,這也是為什么我對定義感到困惑。我剛才在思考,而 Mat 以為我很難過……它說“反射是進程檢查、內(nèi)省和修改自身結(jié)構(gòu)和行為的能力”,所以它基本上涵蓋了一切。如果你從日常語言中的“反射”這個詞來理解的話,這其實是有道理的。

Mat Ryer: 是的,是的。

Jaana Dogan: 所以我認為它不僅僅局限于那小小的一部分……我在我的思維模型中試圖過度限定它,但它其實更廣泛。它涵蓋了所有關(guān)于內(nèi)省和修改結(jié)構(gòu)與行為的內(nèi)容。

Mat Ryer: 對。其實在 Go 中進行類型斷言(type assertion)時,從某種程度上來說,這也是一種反射,對嗎?在運行時你會說“這是某種類型,但我們不知道具體是什么類型,所以我要斷言它是某個特定類型,如果斷言成功,我就可以執(zhí)行某些操作。”從某種程度上來說,這也算是反射,對吧?但這還是發(fā)生在編譯時的,對吧?

Jon Calhoun: 是的,編譯時你確實可以加入更多檢查……但實際的檢查,我猜必須在運行時進行,因為在編譯時你并不知道。

Mat Ryer: 嗯,你說得對。

Jaana Dogan: 是的,斷言是在運行時發(fā)生的,所以你可以說它是一種內(nèi)省操作,實際上它就是反射。但類型系統(tǒng)為我們提供了一個非常好的功能,讓我們能夠以一種更優(yōu)雅的方式實現(xiàn)它,而不是依賴于 reflect 包之類的東西。所以你可以說“是的,這是一個反射功能”,但是它通過語言中的語法糖表現(xiàn)出來。

Mat Ryer: 對,確實如此。它也提供了一些檢查機制,比如你不能進行無效的類型斷言,編譯器在某些時候會幫你做一些檢查。但你說得對,這確實是在運行時完成的,這也正是它的目的。

Jon Calhoun: 是的,從這個角度來看確實很有趣。類型斷言可能是大家最常見的反射使用方式;我想第二種最常見的應(yīng)該是結(jié)構(gòu)體標(biāo)簽(struct tags)。雖然大家可能并不會直接使用它們,但我認為大多數(shù) Go 開發(fā)者至少見過結(jié)構(gòu)體標(biāo)簽,并且可能會想“這是什么東西?” 所以我覺得這是另一個可以深入討論的點,因為我認為這是反射在 Go 中的第二大使用場景。

Mat Ryer: 沒錯。對于那些不熟悉的人來說---尤其是當(dāng)你處理 JSON 數(shù)據(jù)時,你會看到這個現(xiàn)象……你可以在結(jié)構(gòu)體字段名后面加一個字符串,這個字符串可以在運行時解析,當(dāng)然可以從中提取元數(shù)據(jù)。以 JSON 為例,它允許你指定字段名,這樣你可以使用與結(jié)構(gòu)體字段不同的字段名。你還可以選擇不包含某個字段。還有一種特殊的語法,是一個字符串加逗號,這其實是 Go 語言中一個比較奇怪的部分,確實比較獨特。你還可以告訴它如果字段為空,則省略該字段。如果是默認值,它就不會包含在 JSON 對象中。

我記得我第一次看到這個時……當(dāng)時感覺這可能是個臨時的功能,但事實證明它非常有用,尤其是在這種情況下非常有效。不過 Jon,你寫過一個使用結(jié)構(gòu)體標(biāo)簽的項目,對吧?就是那個表單項目。

Jon Calhoun: 是的。

Mat Ryer: 那是什么?

Jon Calhoun: 我做過一些不同的項目……歷史上我在很多項目中都使用過反射。我來自 Rails 背景,而---Rails 本質(zhì)上就是一個大量使用反射的框架。我對那個框架的整體看法就是如此。所以在 Go 中我沒有做得那么復(fù)雜,因為我覺得在 Go 中那樣做并不合適。但我當(dāng)時想寫一些代碼,基本上我想要將一個結(jié)構(gòu)體生成一個 HTML 表單,并且當(dāng)用戶提交該表單時,我希望能夠解析表單內(nèi)容,并將用戶提交的所有值重新放入該結(jié)構(gòu)體中。這樣可以簡化我的工作,我可以在多個處理器之間共享這個表單,并簡化處理流程。

所以我創(chuàng)建了一個使用結(jié)構(gòu)體標(biāo)簽的 Form 包……當(dāng)然還有其他方法可以處理這個問題,我們應(yīng)該討論一下……但當(dāng)時我只是想看看能不能通過這種方式處理問題。結(jié)構(gòu)體標(biāo)簽用于一些地方,比如你需要更改字段名。如果你的結(jié)構(gòu)體字段名是“Email”,但你希望它在表單中顯示為“e_mail”,你可以使用結(jié)構(gòu)體標(biāo)簽來更改這些信息。這就是我當(dāng)時使用它的地方。但這個項目也是一個有趣的實踐,因為它展示了在 Go 中編寫和使用反射是多么令人困惑。

我認為很多人都會在這方面遇到困難。某種程度上我覺得這是有意為之的;他們并不是想讓它變得更糟,而是不想讓它變得那么容易,以至于人們在不必要的情況下就使用反射。

Mat Ryer: 是啊……因為類型安全性帶來了很多好處,這樣做是有道理的,不是嗎?

Jon Calhoun: 是的。當(dāng)時我做這個項目時,可能在使用結(jié)構(gòu)體標(biāo)簽這方面有點過頭了。比如,對于輸入字段的幫助文本或默認值等,我實際上讓你可以通過結(jié)構(gòu)體標(biāo)簽來提供這些值……結(jié)果是,你可能會有一個非常長的結(jié)構(gòu)體標(biāo)簽,附加在某個字段上……看起來有點怪,因為這不是真正的代碼,而是元數(shù)據(jù)。然而,它提供的功能遠遠超過你初看時的感覺。

Jaana Dogan: Mat,你剛才說的很有趣---你第一次看到它時,覺得它幾乎像是一個臨時的解決方案。我當(dāng)時也是這么覺得,因為我也有這些顧慮……比如,Go 是一門非常強類型、簡單的語言,但有時我會覺得自己過度使用了結(jié)構(gòu)體標(biāo)簽……我當(dāng)時還期待一種類似注解的東西;在其他語言中,我們有注解,注解可以是有類型的,它們可以處理更復(fù)雜的情況,而不會犧牲太多類型安全。我本以為 Go 會有類似的東西,這是很久以前---語言誕生之初的想法……但他們希望保持語言的簡潔,因此沒有引入注解。我意識到的是,我并沒有看到太多由于結(jié)構(gòu)體標(biāo)簽引發(fā)的混亂。

我覺得大家通常只在非常特定的情況下使用結(jié)構(gòu)體標(biāo)簽,比如定義 JSON 鍵名之類的。那么你怎么看?你覺得目前的情況足夠了嗎?我們其實不需要注解,還是說這是一個錯失的機會?因為結(jié)構(gòu)體標(biāo)簽難以維護,我們在這方面沒有做得很好,或者我們錯失了一些通過更豐富的方式來注解字段的機會?

Mat Ryer:  是的,這是一個非常有趣的問題,因為在結(jié)構(gòu)體中為特定用途添加一些額外的元數(shù)據(jù)確實有其價值。另一種選擇是直接用強類型來描述同樣的東西。Jon,舉個例子,你可能會有一個地址結(jié)構(gòu)體,里面有不同的字段,然后你通過結(jié)構(gòu)體標(biāo)簽給它們添加標(biāo)簽、占位符和幫助文本等信息。你可能會有一個表單類型和字段類型,這樣寫起來雖然很冗長,但非常清晰,這是它的優(yōu)點。不過我聽說---其實我不太確定---結(jié)構(gòu)體標(biāo)簽的解析速度很慢。這還是一個問題嗎?有沒有對它進行過優(yōu)化,還是說它其實已經(jīng)很快了?

Jon Calhoun: 我不確定,但我從來沒有在一個項目中遇到這種速度問題。如果我正在渲染 HTML 并將其發(fā)送給用戶,那么發(fā)送 HTML 所花費的時間幾乎肯定會遠遠超過解析結(jié)構(gòu)體標(biāo)簽的時間,所以這并不是一個主要的擔(dān)憂。

我還想說,你剛才提到的表單類型---我需要把它整理成一個代碼片段并分享一下,也許我會把它放到節(jié)目的備注里。其實我有兩個版本的實現(xiàn)。一個是用表單包實現(xiàn)的,它接收一個結(jié)構(gòu)體,并生成一些 HTML,如果你提供一個 HTML 模板的話……而另一個版本則是你描述一個表單類型。我有另一個結(jié)構(gòu)體,比如說“這是我的注冊表單結(jié)構(gòu)體”,但我會為它寫一個方法,使用我通用的表單類型,然后生成它應(yīng)該是什么樣子……我知道如何使用模板來渲染它的 HTML。所以在這個版本中,我完全沒有使用反射……你說得對,這個版本確實更冗長,但在某些方面它肯定更好,因為它更清晰地表明了發(fā)生了什么。

不過有時候這也取決于項目的類型……因為對于一些快速的項目,你只是想快速生成一個表單,這時候有一個“這個包可以直接處理”的功能是很不錯的。而在其他情況下,如果這是一個需要長期維護的項目,我們可能需要對內(nèi)容進行更多的定制。這時,選擇一個更容易修改、更冗長的方式可能更有意義,但最終你還是能得到相同的結(jié)果。

Mat Ryer: 是的,我記得 App Engine 舊的數(shù)據(jù)存儲也使用過它們……通常是用于字段名稱,但你也可以指定不希望在某個字段上建立索引,然后將其放入數(shù)據(jù)存儲中。能夠以這種方式注解結(jié)構(gòu)體是非常強大的,這也很合理,因為你確實是在討論該字段的屬性,這是非常直接的。所以是的……還有你提到的類型化注解---我記得 C# 中有這個功能,我想 Java 也有。

這種想法是,你在代碼中有實際的類型,你可以使用這些類型來注解字段。然后,我想你可以檢查這些類型的存在,甚至可以對它們進行程序化處理。這是一種非??岬脑幊谭绞?,同時你可能還能保持較高的類型安全性。

Jaana Dogan: 是的,維護性也更高。你還可以輕松運行查詢。你可以讓編輯器展示“顯示所有使用了此注解的地方”。或者假設(shè)你想修改注解中的某個值,你可以輕松搜索到它,然后在所有相關(guān)地方進行重構(gòu)。所以擁有一定的類型安全性可以讓你做到這些……但正如我所說,我不認為我們在 Go 中過度使用結(jié)構(gòu)體標(biāo)簽。也許是因為它們沒有類型,所以大家都小心翼翼地不去濫用它們……我覺得我們目前保持了一個不錯的平衡,它們被使用得很少。但最大的問題是,由于它們是非結(jié)構(gòu)化的,并且需要解析,維護性和潛在的性能問題讓人擔(dān)憂。

Jon Calhoun:  我完全同意 Jaana 的看法,可能正是因為它們難以維護,人們才不常使用它們……而如果我們引入了類型化的注解,我?guī)缀蹩梢钥隙ㄈ藗儠痊F(xiàn)在更頻繁地使用它們,甚至在一些不適合的場景中也使用它們……因為我見過類似的情況,特別是在使用結(jié)構(gòu)體標(biāo)簽時。我認為有一類問題非常適合使用結(jié)構(gòu)體標(biāo)簽。比如編碼 JSON,或者幾乎任何類似的編碼過程……編碼是一個很好的例子,因為你的結(jié)構(gòu)體可能與實際需要編碼的格式不完全匹配,所以你需要有一種方式來定義它應(yīng)該如何編碼和解碼。而 ORM 也是類似的,當(dāng)你構(gòu)建一個 ORM 時,你可能只想快速地將數(shù)據(jù)插入 SQL 數(shù)據(jù)庫中,并指定 SQL 數(shù)據(jù)庫中的字段名稱---這很合理。

但還有其他一些庫,比如驗證庫,你會在結(jié)構(gòu)體標(biāo)簽中添加類似“此字段為必填”的信息---我并不是說人們不應(yīng)該使用這些庫,但我確實認為這些庫在長期使用中可能會帶來問題。它們可能會導(dǎo)致代碼中充滿各種結(jié)構(gòu)體標(biāo)簽,整個結(jié)構(gòu)體類型變得難以理解,維護起來也很困難。而且沒有編譯器的安全保障。如果我們加入了類型化的注解,我想人們可能會更傾向于使用它們,而不會考慮其他方法。

Mat Ryer: 是的,我見過一些檢查 JSON 標(biāo)簽的 linter。如果你漏掉了一個引號,或者標(biāo)簽的格式不正確,有些 linter 會提醒你“哦,這個標(biāo)簽格式不正確”。雖然不是編譯器做的檢查,因此沒有同樣的安全性,但我想這種不太吸引人的 API 可能也是它不被廣泛使用的原因之一。此外,它確實有點神秘。對我來說,特別是我聽到很多人說,吸引他們使用 Go 的一個原因是它沒有太多“魔法”---它是一門非常清晰簡單的語言?,F(xiàn)在,我可能有點走向了另一個極端,幾乎對任何神秘的東西都過敏,盡管有些人告訴我,我的外表看起來像個魔術(shù)師(笑)。

Jon Calhoun: 是的,神秘的部分確實讓人頭疼……我記得第一次使用結(jié)構(gòu)體標(biāo)簽時,我在學(xué)習(xí) Go。我當(dāng)時在做與 MongoDB 相關(guān)的事情……你會用 Bison 來定義結(jié)構(gòu)體標(biāo)簽……當(dāng)時我在設(shè)置結(jié)構(gòu)體標(biāo)簽,腦子里在想“我需要導(dǎo)入什么包才能讓它工作嗎?為什么我的代碼沒有導(dǎo)入任何東西卻還能正常工作?”

Mat Ryer: 哦,是的。

Jon Calhoun: 當(dāng)時這讓我非常困惑,因為我心想“我不明白這段代碼是怎么編譯通過的?!敝钡胶髞砦疑钊胙芯苛艘幌?,才明白過來,但當(dāng)時真的覺得這像是某種魔法,剛開始學(xué)習(xí)時這讓我有點沮喪……因為我不知道發(fā)生了什么。

Mat Ryer: 其實它就是一個字符串,對吧?

Jon Calhoun: 是的,但你會想“肯定是編譯器在做什么事情吧?它肯定是在某個地方寫了些什么東西”,所以你會想“這到底是怎么回事?”這讓我困惑了好一陣子。直到后來我意識到“哦,他們只是解析這個字符串,Bison 包在使用時才會處理它”,這樣就說得通了。但當(dāng)時我真的很困惑。

Mat Ryer: 是啊。其實 reflect API 對結(jié)構(gòu)體標(biāo)簽的解析還不錯,API 很簡單。因為 reflect 包中的一些功能---它實在是太“元”了。有些功能你可以理解,比如你可以獲取某個值,而這個值是一個結(jié)構(gòu)體。在 reflect 包中,它是一個強類型的值,這個值描述了這個值的類型。然后由于這些值可以是許多不同類型的東西,你會看到很多方法,其中大多數(shù)情況下這些方法是非法調(diào)用的。

比如你試圖獲取一個整數(shù)的長度,當(dāng)然,reflect 包中有這些方法可以做到這一點。所以在編譯時你可以調(diào)用它,但只有在運行時你才會發(fā)現(xiàn)你不能獲取一個整數(shù)的長度。類似的例子還有很多,因此你會檢查所有東西。當(dāng)你寫防御性代碼時會非常冗長,以確保你不會遇到任何這些運行時的奇怪問題。當(dāng)然,測試也能幫助你避免這些問題,但……

Jaana Dogan: 是的,你提到了測試,但這其實也很難測試。沒有一組標(biāo)準的測試用例。我曾在一些數(shù)據(jù)庫包上工作過,由于 Go 沒有泛型---也許我們可以在這個對話的背景下討論一下這個問題---我們通常會大量依賴接口和類型轉(zhuǎn)換。如果你有一個接口切片,它可以是一個值或一個指針,或者是指針的指針,然后你必須通過 reflect 包來處理所有這些魔法,而 reflect 包本身已經(jīng)非常冗長了,所以包裝和解包所有這些類型非常困難。我找不到一個簡單的測試方法,因為沒有一組標(biāo)準的測試用例。比如,如果標(biāo)準庫提供了一些類似“嘿,請考慮測試這些”的東西,或者提供了一份測試的清單,那將會簡單得多。

Mat Ryer: 是啊……因為你可能需要測試所有不同的類型之類的東西……當(dāng)然,還有數(shù)組和切片。

Jaana Dogan: 沒錯。

Jon Calhoun: 很有趣的是……Mat,你提到的一個比較簡單的用例是獲取某個值……有趣的是,這是我第一次使用 reflect 庫時遇到的問題之一。因為當(dāng)有人傳遞了一個值,比如一個字符串,你會想“好吧,我要獲取這個值?!边@很合理,你的代碼也能正常運行,一切看起來都沒問題。是的,像長度這樣的東西可能不適用,但大多數(shù)情況下會正常工作。但當(dāng)有一天有人傳遞了一個 nil 指針,它有一個類型,但它是一個 nil 指針,你的代碼突然就崩潰了,你會想“剛剛發(fā)生了什么?” 你就會遇到這些奇怪的情況,如果類型是指針,并且它是 nil,那么你需要使用 reflect.new 來實例化一個新元素。如果它是一個接口,你需要獲取它所指向的底層元素類型,因為接口本身并沒有多大幫助……

會有很多這些奇怪的情況,表面上看起來很簡單,比如“我只是想獲取這個值”,但實際上并不是那么簡單。所以你最終會遇到很多邊緣情況……即使你把它搞定了,并為這些情況寫了測試,比如傳遞了一個空指針,它有一個類型。我們傳遞了一個空接口,我們傳遞了設(shè)置了實際值的接口---你為所有這些情況寫了測試,但到最后你仍然在想“我是不是還遺漏了某些邊緣情況”,因為幾乎不可能沒有遺漏。

Mat Ryer: 是的,從某種意義上說,這就像是在泄露 Go 內(nèi)部的工作原理。如果你使用 reflect,你確實會學(xué)到很多關(guān)于類型系統(tǒng)的東西……但坦白說,我經(jīng)常處于一種“試錯”的狀態(tài),依賴 TDD(測試驅(qū)動開發(fā))來告訴我是否做對了。如果我使用了 reflect 包,我通常會寫一些代碼,比如調(diào)用 Elem() 獲取元素,然后出于某種原因(我不確定是什么),我必須再次調(diào)用 Elem()……我知道這里面一定有一個很好的解釋,但我不知道是什么解釋,我也沒時間去深究,我只知道如果我調(diào)用 elem.Elem(),在這種情況下我就能得到我需要的東西,因為測試通過了……所以當(dāng)涉及到反射代碼時,我往往采取一種非常蠻力的方式,這種感覺不太好。

Jon Calhoun:  是的。我不常使用 TDD,但使用 reflect 時,這是我?guī)缀跻欢〞褂?TDD 的情況,因為我會想“這是我知道我要傳入的所有不同類型,它們都需要正常工作”,從這開始會輕松得多。否則你會想“是的,這起作用了”,然后你運行它,結(jié)果什么都不工作,你會想“我不知道發(fā)生了什么?!?/p>

我剛剛看了一些我使用 reflect 寫的代碼,我看到同樣的情況,就是 .type.Elem(),然后你創(chuàng)建了一個 reflect.New(),使用了那個類型,然后又調(diào)用了 .Elem(),你會想“看著這段代碼,我完全不知道為什么我要這么做。我只知道它能工作”,這感覺真的很奇怪。

Jaana Dogan: 有一件事我意識到的是,我覺得 Go 當(dāng)前的類型系統(tǒng)在一定程度上加劇了這些問題……因為我們不得不依賴接口作為參數(shù),或者接口切片作為參數(shù),然后就會出現(xiàn)大量的類型轉(zhuǎn)換問題。我們無法限制用戶想要做什么,或者用戶想要傳遞什么……你必須處理所有這些情況,才能讓你的庫正常工作。

Go 中的一個典型例子是,我們有一些庫會根據(jù)用戶傳入的參數(shù)進行類型轉(zhuǎn)換,比如用戶傳入了某個接口類型的值……它可能是一個結(jié)構(gòu)體,也可能是一個指向結(jié)構(gòu)體的指針,或者是一個數(shù)組之類的東西,但它必須通過類型轉(zhuǎn)換來了解類型,因此你不能傳遞一個普通的 nil,而是必須傳遞一個有類型的 nil。所以 Go 有一些奇怪的地方,加上沒有泛型,這就導(dǎo)致所有這些復(fù)雜的情況需要由庫通過 reflect 包來處理,我認為這加劇了我們所經(jīng)歷的所有這些 .Elem() 的問題,而我們并不完全理解它們的原因……整個語言在某種程度上加劇了這個問題。

Jon Calhoun: 你提到的這一點很好---如果你在使用 reflect,你幾乎總是要接受空接口作為參數(shù)。這幾乎總是你的參數(shù)類型,而這通常是編寫代碼時的一個不好的信號。

Mat Ryer: 是的,但就像你說的,在某些情況下這是無法避免的……

Jon Calhoun: 是的。

Mat Ryer: 我們很多人每天都在使用的一個東西就是 JSON 的編解碼功能。你可以傳遞任何類型,因為它可以解碼為你編寫的結(jié)構(gòu)體類型……或者更常見的是解碼為一個 map[string]interface{}。它完全沒問題。而且,reflect 包也可以實例化東西,對吧?如果你傳遞了一個 map,它會為你創(chuàng)建 map……類似的事情。所以它確實變得有點奇怪。我記得以前我想寫一個用于 testify 的 mock 庫,當(dāng)時我真的很想在運行時從接口或另一個結(jié)構(gòu)體創(chuàng)建一個 mock 結(jié)構(gòu)體。當(dāng)時你做不到,但從那以后我見過---我不確定現(xiàn)在是否可以,但我見過一些函數(shù)和方法,似乎現(xiàn)在你可以實例化結(jié)構(gòu)體之類的東西;我得再確認一下……但這是非常強大的。如果你考慮到我們沒有泛型,確實很有誘惑力去看看能不能通過 reflect 來完成這項艱難的工作,這樣你就能得到一個非常智能的動態(tài)功能……這將非常有趣。

在測試代碼中,你也許可以容忍它不是那么高效---它不會出現(xiàn)在一個緊密的循環(huán)中;當(dāng)然,你不希望測試代碼變得慢。但測試代碼并不是總是處于低延遲的場景中,盡管我們?nèi)匀幌M麥y試代碼運行得相對較快……

Jon Calhoun: 是的,就像 Jaana 說的,Go 的類型系統(tǒng)有它的局限性,而你提到了 JSON 編碼……我在想,即使在你知道必須傳遞指針的情況下,你也不能只傳遞結(jié)構(gòu)體;你必須傳遞指向結(jié)構(gòu)體的指針來獲取返回值……如果有一個類型系統(tǒng)可以讓你限制這種情況,那會更好,但由于現(xiàn)有的設(shè)置方式,它做不到。相反,你必須依賴錯誤處理之類的東西……這并不是說 Go 是個糟糕的語言,只是有時你會感到掙扎,特別是在看到這些限制時,我相信這對一些人來說是很困惑的。

Mat Ryer:  是的。如果 JSON 包不使用 reflect,它會是什么樣的呢?你幾乎肯定會有某種回調(diào)機制,但你仍然會有接口,因為你不知道值的類型。


Jon Calhoun: 是的,幾乎必須是類似于“編碼這個”的方式,然后不是說“傳入一個接口”,而是必須說“它必須是一個指針”。必須是類似這樣的東西。不過即使這樣,也有點讓人困惑,因為map并不總是這樣工作的,如果我沒記錯的話。我記得你可以直接傳入一個 map,而不一定非得是一個指針,但我不太記得了……它必須是一個指針嗎?

Jaana Dogan: 是的……

Jon Calhoun: 我已經(jīng)很久沒有往那里面?zhèn)魅?map 了……我應(yīng)該去檢查一下。

Mat Ryer: 哦,不……那你都傳入了什么?

Jon Calhoun: 結(jié)構(gòu)體……

Mat Ryer: 哦,沒錯。那很合理。

Jon Calhoun: 我大多數(shù)時候都是解碼到結(jié)構(gòu)體中。

Mat Ryer: 嗯,如果你寫的東西是你不知道數(shù)據(jù)結(jié)構(gòu)的---你知道,很多 API 確實是這樣做的。

Jon Calhoun: 是的。

Mat Ryer: 這確實是個危險的領(lǐng)域。但如果你完全不知道實際的類型……我寫過一個小工具---它還沒完成,雖然它大體上能工作,但絕對還沒準備好……它基本上是一個假的 JSON 數(shù)據(jù)生成器。所以你可以傳入任何數(shù)據(jù)---實際上,你可以傳入一個結(jié)構(gòu)體,它會生成很多該結(jié)構(gòu)體的示例,它使用 JSON 來實現(xiàn),因為至少在 API 里,JSON 的編組和解組是非常簡單的事情。所以在那種情況下,是的。如果這是一個托管在網(wǎng)站上的 API,你會希望人們能夠傳入任何種類的 JSON,包括對象數(shù)組以及單個對象……然后它可以根據(jù)這些數(shù)據(jù)生成一些測試示例數(shù)據(jù)。這就是這個想法。這個工具非常“元”。

所以這種用例并不常見,我想……但我認為 JSON API 在某種程度上非常好用,特別是作為一個用戶。如果沒有 reflect,你最終會得到一個函數(shù),它給出的鍵是字符串,值可能是一些字節(jié),然后你必須根據(jù)你對具體情況的了解來解組這些字節(jié)。所以標(biāo)準庫為我們做這些事情確實很好。順便說一句,如果沒有這些功能,我認為這會損害 Go 的聲譽。想象一下,如果有篇 Hacker News 的文章說你必須手動處理 JSON 的編組和解組……

Jaana Dogan: 是的,如果是那樣,Go 可能不會被如此廣泛地采用。

Mat Ryer: 沒錯,絕對是這樣的。

Jon Calhoun: 即使按照現(xiàn)在的方式,JSON 仍然有一些難點……就像你說的,你有結(jié)構(gòu)體,但---我覺得 Stripe 是個例子,支付來源可以是信用卡或銀行賬戶,所以你會有一個可以不同的對象數(shù)組,你需要自己寫一個類型來正確地解組它……所以你不得不為此寫一些自定義的代碼。我想如果你根本沒有 JSON 包,那將是一場噩夢,充滿了人們的抱怨和“這太糟糕了”的評論。即使在這種情況下,當(dāng)你不得不寫自定義代碼,我仍然盡量利用 JSON 包的功能……比如,創(chuàng)建一個只包含我想要的字段的結(jié)構(gòu)體,解組它,弄清楚它是什么,然后傳入相應(yīng)類型的結(jié)構(gòu)體……這讓我省去了自己處理“如何解組”的實際開銷。

Jon Calhoun: 剛才,Mat,你提到了結(jié)構(gòu)體標(biāo)簽,Jaana 和我在節(jié)目開始前討論了一下。我覺得結(jié)構(gòu)體標(biāo)簽如果能成為一個單獨的庫,可能會帶來一些好處……因為這樣你可以把它分離出來……我覺得結(jié)構(gòu)體標(biāo)簽是反射中最安全的部分,你導(dǎo)入 reflect 包后,其他部分可能不一定更糟,但絕對有些嚇人……所以有一個邊界,你可以只處理結(jié)構(gòu)體標(biāo)簽---把它分離出來,可能會有所幫助,比如“好吧,我這里只是在看結(jié)構(gòu)體標(biāo)簽。”

Mat Ryer: 是的,我懂你的意思,這樣你就不必把整個 reflect 包導(dǎo)入到代碼中。而且我認為 reflect 包中也包含了 unsafe,盡管很多非常普通的包也確實有 unsafe……但我懂你的意思……你可以只導(dǎo)入一個解析結(jié)構(gòu)體標(biāo)簽的包,比如 reflect/struct tags 之類的。我還挺喜歡這個想法的。你應(yīng)該告訴別人這個想法。

Jaana Dogan: 我記得 Go 早期的時候,他們說“嘿,如果你導(dǎo)入了 reflect 包,那可不妙?!?那時候幾乎認為這是不安全的,因為你也依賴于 unsafe 出于很多其他原因……但你知道,那是你不應(yīng)該看到的導(dǎo)入行之一,或者你應(yīng)該非常謹慎;如果你使用它,你應(yīng)該非常小心地控制它的用法,等等。但你知道,突然之間,大家開始導(dǎo)入 reflect,因為它做了很多基礎(chǔ)性工作,比如結(jié)構(gòu)體標(biāo)簽……所以我覺得如果它是一個單獨的包,用戶的心理上可能會有更多的分離感。這樣你可以編寫 linter 工具來捕捉 reflect 的導(dǎo)入……但一些基礎(chǔ)或更簡單的擔(dān)憂可以存在于不同的包中。

我見過的一些與此相關(guān)的做法是,如果人們想依賴 reflect 包,他們不會到處導(dǎo)入它;他們只是把 reflect 的所有用法封裝在一個單獨的包中,然后從那個包中提供一些工具。你見過這樣的做法嗎,或者你做過類似的事情嗎?

Mat Ryer: 沒有,但這對我來說很有道理。至少這樣你就把所有的怪異集中在一個地方……但我不確定這是不是一種健康的做法,因為這有點像“廚房水槽”或“工具包”那種……

Jon Calhoun: 我確實做過將所有 reflect 相關(guān)的代碼放在一個源文件中的做法,但我從來沒有在 Go 的反射中做過足夠大的東西,需要走到那一步……不過,我確實可以說,我在 Ruby 中確實瘋狂使用過一些元編程的東西,但當(dāng)我轉(zhuǎn)到 Go 時,我并不覺得那是寫 Go 代碼的正確方式,所以我盡可能避免它。

Mat Ryer: 是的。我見過一個例子,有人為了做一個好公民,他們打算把一些數(shù)據(jù)放到 map 里,但如果 map 是 nil 的話,程序就會 panic……所以他們實際上使用了(我認為是)JSON 解組器;如果 map 是 nil,它會直接解組---他們在代碼中直接寫了兩個小花括號,表示一個空對象。然后它使用這種技術(shù)創(chuàng)建了一個 map……這意味著作為程序員,你可以傳入一個 nil map,它仍然能工作。不過,這有點太“魔法”了,而且有時讓它 panic 也沒什么問題,或者……因為它是一個庫,有時我不介意捕獲那些會導(dǎo)致 panic 的情況,然后用一個更好的錯誤信息來 panic,比如“你必須在傳入之前創(chuàng)建 map”之類的。

但我確實見過一些不必要使用 reflect 的情況,但人們?yōu)榱擞脩魢L試多做了一些。這些情況挺有趣的。

另一種反射的形式是 Go 中的 AST 包,和一些實際的代碼反射、代碼分析包……它們也在不斷改進。剛開始的時候,它們非常難用,現(xiàn)在有一些更高層次的包讓它變得更容易了。我們有一個項目,我們實際上用 Go 接口描述了我們的 API,我們使用了 AST 包---有一個 packages 包可以讓你打開一個包,然后你可以遍歷接口,檢查接口中的字段之類的東西……所以它做了那種反射;它以自己的結(jié)構(gòu)表示數(shù)據(jù),然后使用這些數(shù)據(jù)從模板生成代碼。

所以這很棒,因為我們所有的 API 都是用 Go 接口描述的,作為 Go 開發(fā)者,這對我們來說非常容易理解和推理……而且它是真正的 Go 包,因此也是類型安全的。你不能使用無效的類型,所以這是描述 API 的一種很棒的方式。你知道它會工作。我們可以從中生成客戶端,生成服務(wù)器端代碼,以及處理所有那個樣板代碼的 HTTP 邏輯……任何樣板代碼都可以生成,我們甚至生成了另一個接口,實際上和原來的接口略有不同,因為它接受一個上下文,并返回一個錯誤,而我們在定義時省略了這些。

所以我們可以寫下我們的定義接口,運行代碼生成器,然后實現(xiàn)接口,僅此而已。我們就有了一個新的服務(wù),然后可以在我們的項目中公開了。

Jaana Dogan: 你們什么時候開源這個項目?

Jon Calhoun: 我想他已經(jīng)開源了。

Mat Ryer: 是的,已經(jīng)開源了,叫 Oto[5]

Jaana Dogan: 真的嗎?!

Mat Ryer: 是的,叫 Oto。

Jaana Dogan: 很棒。

Mat Ryer: 目前它基本上是一個 JSON API,但實際上,因為它只是代碼生成,模板也可以修改,所以你可以很容易地為它編寫一個二進制協(xié)議,或者其他任何類型的協(xié)議。是的,挺不錯的。有人為它寫了一個 Rust 服務(wù)器模板……這有點怪,但也挺酷的。我們會把鏈接放在節(jié)目筆記里。它的地址是 github.com/pacedotdev/oto[6],我會把它放在節(jié)目筆記里,供有興趣的人參考。我們在生產(chǎn)環(huán)境中使用它,效果非常好。我是說,我們的用例相對簡單,但它真的很好用……這其實也是一種反射,因為我們必須以編程方式檢查那些接口,然后對它們進行操作。

Jon Calhoun: 所以 Mat,我猜你是用 Oto 來生成代碼的,對吧?

Mat Ryer: 是的,基本上就是這樣。它接收 Go 接口,把它們和模板混合,然后生成新的代碼。

Jon Calhoun: 我想說的是,我們經(jīng)常說反射不好,或者你應(yīng)該盡量避免它,因為它很難理解,也很難維護……但我覺得有時候這很難,因為我們沒有告訴人們替代的方法……而我認為代碼生成是一個非常有用的替代方案。就像你說的,你其實是在做反射的事情,分析代碼,然后生成代碼,最終得到的東西更容易管理。

我甚至見過一些 ORM 采用這種方法;我記得 SQLBoiler[7] 就是其中之一,它會掃描你的 SQL 數(shù)據(jù)庫,然后從中生成 Go 結(jié)構(gòu)體……所以它不是使用反射,而是直接生成與你的數(shù)據(jù)庫完全匹配的東西,你可以直接使用它們……這是一種完全不同的做法,但我認為限制反射的使用讓人們?nèi)タ紤]其他方法,并決定“這是更好的選擇嗎?這是更容易維護的嗎?”

Mat Ryer: 是的。還有 go generate 命令;你可以在代碼中加一個注釋,一個特殊的注釋……這有點像魔法,但你可以寫 //go:generate,然后加上一條命令。如果你在項目中輸入這個命令,它就會執(zhí)行這些命令。這對于那種需要在構(gòu)建前生成代碼的情況非常有用……這是一個不錯的做法,因為你可以獲得類型安全,編譯器會幫助你;也許一開始沒有,但一旦代碼生成出來,它通常就是你項目的一部分了,接著就可以構(gòu)建了……如果它有問題,你很快就會發(fā)現(xiàn)。

Jaana Dogan: 是的,這正是我想說的……我認為 ast 包和 reflect 包的區(qū)別在于,ast 包是一種美學(xué)上的選擇;它并不是在運行時執(zhí)行的。所以你生成代碼后,仍然具有類似的可維護性和類型安全性。你只是用編譯器生成了一些代碼。如果你能將一些問題交給代碼生成來解決,那絕對是值得做的。

Mat Ryer: 是的,這個觀點很好。

Jon Calhoun: 我們之前討論過泛型問題,其實我很想看到一種泛型的實現(xiàn),基本上在一開始就運行 go generate。你寫的代碼就像泛型已經(jīng)存在一樣,按照某個提案去寫,然后它會在某個預(yù)處理步驟中將其編譯成 Go 代碼,生成需要的內(nèi)容,然后繼續(xù)處理……我認為這是可行的,雖然需要一些復(fù)雜的工作。

Mat Ryer: 我和我的一個朋友寫了一個類似的項目,叫 Jenny。有人在用它。它使用一種特殊的類型,基本上就是一個接口類型,放在一個不同的包里……我想這還是用了 AST 的技術(shù);它會找到這些實例,并查找出你在命令中列出的類型。你運行一個命令,列出你想支持的類型,然后它就是一個復(fù)制粘貼的過程,替換掉代碼中提到的那些類型。它不是完美的,因為你不能對它進行類型斷言;一旦那樣做,它就很奇怪了……但在簡單的情況下,它是有效的。我想這就是你說的那種東西。

Jon Calhoun: 我用過類似的東西……我在想的是,可能可以把這個想法拓展得更遠,像現(xiàn)在的泛型提案那樣。讓你可以完全按照泛型的方式寫代碼。因為泛型的一個問題是,它讓編譯和其他步驟變得更加復(fù)雜。所以,與其把它直接集成到編譯器中,不如在預(yù)編譯步驟中處理它,使其看起來像是已經(jīng)內(nèi)置在語言中,但實際上并不是,這樣在那個步驟進行轉(zhuǎn)換……

Mat Ryer: 對。

Jon Calhoun: 當(dāng)然,這可能會變得非常麻煩,以至于不值得去做……

Jaana Dogan: 對我來說,泛型一直像是“嘿,這里有個模板,你用它生成一些東西,編譯器處理所有這些事情,因為生成的代碼復(fù)雜得讓人難以理解?!?這就是為什么語言需要提供一些語法糖,讓你能夠與這些類型進行交互。所以我想,如果你暴露出生成的內(nèi)容,用戶會覺得非??膳隆D銜懈鞣N不同的---類型,和各種不同的情況,等等……所以我覺得對于很多情況來說,這看起來不會很好。這可能會讓人們一開始就不愿意使用泛型。

這就是為什么我在等待真正的泛型提案和實現(xiàn),我想看看那個語法糖到底是什么樣子……即使實際的難題對我來說是不可見的,至少---我對底層生成的東西其實不感興趣,因為我知道它在很多情況下會非常復(fù)雜。

我認為這些代碼生成器沒有真正流行起來的原因之一是,你需要一種官方認可的泛型解決方案。作為一個庫,我不能隨便選擇一個工具,而不是另一個。實際上沒有太多的實驗。你不能真的暴露底層的東西;我只想要一個對所有人都有效的東西,這樣我們可以達成共識,所有的庫系統(tǒng)都能切換到它……我不太關(guān)心底層生成的是什么,它們可以隨時優(yōu)化,或者做其他事情……在這個領(lǐng)域已經(jīng)有很多工作被做了,所以我們不是在第一次嘗試解決這個問題。

我認為我們應(yīng)該找到泛型的解決方案。它應(yīng)該是官方語言的一部分。我覺得不需要太多的實驗……但這對人們來說會很難,因為它肯定會讓語言變得更加復(fù)雜。

Mat Ryer: 是的,但是你知道,很多 JavaScript 庫采用了一種方法,就是使用一個墊片(shim)……我記得最初的 TypeScript 或者 Google 的 Dart 最初只是編譯成 JavaScript,雖然看起來很丑,但 Jaana,你只需要不要看它。只要把最后的文件命名為“別看這個.go”之類的。

Jaana Dogan: [笑]

Jon Calhoun: 我覺得你必須訓(xùn)練人們……這有點像你在編譯器之上又構(gòu)建了一個編譯器,而它才是給你報錯、與你互動的那個……然后無論它最終編譯成什么,你都得把它藏在某個捆綁文件夾里之類的……

Mat Ryer: 是的,但你想想 IDE 和所有工具……一旦“語法無效”,所有工具就都壞了。

Jon Calhoun: 現(xiàn)在會更難。但隨著他們對不同 IDE 如何使用語言服務(wù)器的改進,希望這種實驗……

Jaana Dogan: Gopls,是的。

Jon Calhoun: 是的?;旧?,因為它們都在使用一個通用的……我忘記叫什么了,但基本上就是語言服務(wù)器---有一個通用的規(guī)范,所有語言都可以實現(xiàn)……所以希望這種工作能帶來更多在現(xiàn)有語言上進行實驗的可能性……這會很有趣。

Mat Ryer: 你能簡要介紹一下什么是語言服務(wù)器嗎?我記得它叫 LSP,對吧?語言服務(wù)器協(xié)議。

Jon Calhoun: 聽起來是對的。一般的想法是,與其讓每個 IDE 或編輯器都自己實現(xiàn) Go 的自動補全和 JavaScript 的自動補全,不如標(biāo)準化它。我記得 VS Code 是第一個這么做的,但現(xiàn)在其他編輯器也在用了。

Jaana Dogan: 是的,我覺得它來自微軟,我不太確定……

Mat Ryer: 是的,確實來自微軟的 Visual Studio Code。

Jon Calhoun: 這個想法是,他們提出了一個類似于 Go 中接口的東西,或者說“這是一個 LSP 應(yīng)該為某種語言提供的東西?!?基本上,它應(yīng)該有一些你需要實現(xiàn)的方法,它可以根據(jù)用戶所在的位置給出自動補全建議……想法是你可以為任何語言實現(xiàn)這個,然后任何 IDE 或編輯器都可以利用它來實現(xiàn)編輯器中的自動補全。

Mat Ryer: 是的,這太棒了……老實說,我真的無法相信它能工作,因為所有語言差異這么大。我們是怎么找到一個協(xié)議,能夠描述所有這些的?我覺得這真的挺神奇的,毫無疑問,這個協(xié)議肯定不簡單。

Jon Calhoun: 這大概是那種1%的極端情況處理得不太好,但對大多數(shù)開發(fā)者來說,這并不重要,LSP 的好處遠遠超過了這些問題。

Mat Ryer:  是的。

Jon Calhoun: 但仍然存在一個問題---這不一定是問題,不同的編輯器會用不同的方法來實現(xiàn)這個。我記得 GoLand 是其中一個不使用語言服務(wù)器的編輯器;他們完全使用內(nèi)部的實現(xiàn)。在某些方面,這有好處……因為 Gopls 剛出來時,確實很脆弱。但我記得他們的方案在當(dāng)時對 Go modules 的支持要更好,不過現(xiàn)在我不確定是否還是這樣。

Mat Ryer: 我聽說過很多好評。

Jaana Dogan: JetBrains 通常就是這么做的。他們一切都自己做,這是他們的特色。

Mat Ryer: 是的。我唯一的感受是,每次我不得不使用 Java 時,我都接觸過 Eclipse IDE,真的……這是一種美學(xué)上的感受。我用 Visual Studio Code 是因為它看起來更好看,你花那么多時間在里面……我覺得這確實很重要。我覺得你想要一個美好的使用體驗。但我聽說 GoLand 編輯器有一些很棒的功能,能做很多事情。我還沒試過,但……嗯,我會感興趣。如果有人想給我發(fā)推特,告訴我他們的體驗,我可能會讀一讀。

Jon Calhoun: 可能會。

Mat Ryer: 你猜現(xiàn)在是什么時間……?[笑]

Jon Calhoun: 我想我們都知道,可能是“非主流觀點時間”。

Mat Ryer: 是的,非主流觀點時間!

Mat Ryer: 那么,有沒有人有不太受歡迎的觀點呢?我們已經(jīng)說過的一些事情可能有點不太受歡迎,但……你們有什么特別想分享的嗎?

Jaana Dogan: 我有一個……

Jon Calhoun: 繼續(xù)吧,Jaana。我讓她來主導(dǎo)這部分。

Jaana Dogan:  我覺得我們需要泛型。我知道這可能不是一個非常不受歡迎的觀點,但……我從這門語言一開始就這么說了,結(jié)果大家都討厭我……但我覺得我們確實需要泛型。

Jon Calhoun: 我完全同意,因為我做過足夠多的工作---我覺得一個例子是,如果 Go 想在教育領(lǐng)域表現(xiàn)得好,比如讓人們在大學(xué)里學(xué)習(xí)它,他們會接觸到數(shù)據(jù)結(jié)構(gòu),而沒有泛型很難處理數(shù)據(jù)結(jié)構(gòu)……我認為這是 Java 在學(xué)校里被廣泛教授的原因之一,因為它在這方面做得很好。

Mat Ryer: 是的。你怎么看最近的泛型提案?

Jon Calhoun: 我看過的最近一個提案我挺喜歡的---我還沒深入研究過,但對我來說看起來沒問題……我對細節(jié)要求不高,我的需求相對簡單。

Mat Ryer: 我覺得他們在設(shè)計上取得了很大進展……當(dāng)然,你經(jīng)常會聽到大家真正關(guān)心的是如何實現(xiàn)它,以及這對 Go 的類型系統(tǒng)會有什么影響,維護起來會有多難,等等……聽到 Go 團隊和其他貢獻者優(yōu)先考慮這些問題真是太好了,因為這確實至關(guān)重要。我非常不希望看到 Go 變得太復(fù)雜,以至于我們再也不能添加新功能了……所以在這一點上,我和你站在同一陣線,Jaana。

Jaana Dogan: 其實,我已經(jīng)對這個話題感到非常疲憊了,一年前就不再關(guān)注這些提案了。

Mat Ryer: 哇。你是因為情緒波動太大嗎?

Jaana Dogan: 并不是情緒波動大,而是我對每個提案都有至少 50 個顧慮……

Mat Ryer: 哦,才 50 個顧慮……

Jaana Dogan: 而且沒有簡單的答案……是的,我是說,有些是宏觀層面的……

Mat Ryer: [笑] 你讓其他人情緒波動了。

Jaana Dogan: 沒錯。我覺得我并沒有真正為討論做出貢獻……而且我心里所擔(dān)憂的那些點,你無法真正預(yù)見實際情況會是什么樣子,因為這真的取決于那些會使用泛型的人……隨著時間的推移,我們會看到它對整個庫生態(tài)系統(tǒng)的實際影響。所以我當(dāng)時覺得,“嘿,我并沒有真正為這次討論貢獻什么?!?我很興奮它正在發(fā)生。那些人---我?guī)缀蹩梢钥隙ㄋ麄兎浅T谝?。他們比我更在意,所以我覺得沒有必要再試圖參與了。

Jon Calhoun: 這讓我想起了類型別名,以及它當(dāng)時受到了多少抵制,大家都說它會毀掉這門語言……但自從它被引入后,我?guī)缀鯖]有看到它被廣泛使用---偶爾你會看到它,我也做過一些奇怪的事情,只是為了看看能做些什么……但我覺得我沒遇到過濫用它的庫,這有點有趣,因為你之前看到的強烈反對意見……我理解他們的顧慮,我并不是說人們不應(yīng)該表達顧慮,只是有趣的是,這些擔(dān)憂根本沒有成為現(xiàn)實。

Mat Ryer: 是的,但它確實被引入了。

Jaana Dogan: 我剛好想舉這個作為例子,因為那是我第一次感到對這個項目感到倦怠的時刻,我當(dāng)時想去做其他事情……一切都變成了關(guān)于各種可能性的無盡討論……所以對于泛型---我不想再參與,因為已經(jīng)有太多聲音了,而這是一項艱難的工作,你無法預(yù)見未來。

我也信任 Go 開發(fā)者,因為很多人非常注重簡單性,所以他們不會濫用某個特性。我認為 Go 的用戶對于自己想使用的語言核心子集非常了解……這門語言并不大,但我也信任更大的生態(tài)系統(tǒng)。所以我現(xiàn)在不再那么擔(dān)心了。

Mat Ryer: 是的,我的意思是,你總是可以選擇不用它……坦白說,我直到很晚才意識到---類型別名已經(jīng)進入了這門語言。我記得當(dāng)時的提案和大討論,但……另一個提案是關(guān)于錯誤處理的 Try 提案;對我來說,我對它過敏,因為它感覺太像“魔法”了……而且我覺得它不符合 Go 的哲學(xué)。我們必須小心,不要因為喜歡 Go 現(xiàn)在的樣子而過于僵化,不允許任何演變,但……我覺得你是對的,Jaana---我們確實意識到簡單性和反對“魔法”的重要性。我們作為一個社區(qū),對此非常清楚。而且,是的,我想你總是可以選擇不使用它,如果你不喜歡的話……

Jon Calhoun: 我覺得這可能是為什么反射是一個如此奇怪的話題的原因,因為人們來自其他語言,在那些語言中使用反射是完全正常的。我想我在我們開始錄音之前已經(jīng)說過了,但我不認為如果沒有反射和元編程,Ruby 會如此受歡迎。Rails 和你能用它做的所有瘋狂的事情,都是元編程的副產(chǎn)品,而在很多方面,它讓那門語言更具生產(chǎn)力……但所有這些在 Go 中完全沒有意義。

所以當(dāng)人們從那樣的語言轉(zhuǎn)到 Go 時,他們會想,“為什么這個 reflect 庫這么難用?為什么每個人都告訴我不要用它?”,這是一個很難的心理轉(zhuǎn)變,我覺得這是因為解決問題的方法不同,優(yōu)先級也不同。

Mat Ryer: 是的,我覺得你說得對。有時我會---與其維護……比如有一段代碼用了反射,與其維護它,我會重寫它,因為對我來說,重寫的過程是我弄清楚發(fā)生了什么的方式。這就是問題所在---對我來說,維護它其實很難,我會放棄,轉(zhuǎn)而選擇重寫它……坦白說,我如果可以的話,通常都會這么做,因為我總是發(fā)現(xiàn)重寫是獲得更好版本的方式……你在重寫過程中學(xué)到了很多,第二次寫的代碼總是好得多。但我不知道,這確實是一個有趣的問題。

好吧,我想今天的時間就到這里了……下周我們有一個非常有趣的節(jié)目。我們會邀請一個剛剛得到 Go 開發(fā)工作的人,還會邀請一個正在學(xué)習(xí) Go、還在高中里的學(xué)生。我們會看看人們是如何進入我們稱之為編程的這個瘋狂世界的。

Jon,非常感謝你。Jaana,總是很愉快。我們下次見!

Mat Ryer: 就這樣。

Jon Calhoun: 我本來想彈吉他的……

Mat Ryer: 我得做個空氣吉他,對吧……

Jon Calhoun: 你背景里有吉他,但你卻說:“不彈……”

Mat Ryer: 是的,但我不會在 [聽不清 01:01:02.08] 上彈。Jaana,你曾同意過我看起來像個魔術(shù)師。你還記得嗎?

Jaana Dogan: 是的,你看起來像個魔術(shù)師。

Mat Ryer: 是的。你知道這有多難嗎?你覺得理所當(dāng)然,但我不得不跟我父母坦白。我說:“媽媽,爸爸,坐下。選一張牌吧?!?/p>

Jaana Dogan: [笑] 但你知道嗎,這種胡子造型,這種風(fēng)格……非常魔術(shù)師的感覺。

Mat Ryer: 確實很像魔術(shù)師的。太荒謬了。我應(yīng)該換個造型,但……

Jaana Dogan: [笑]

Jon Calhoun: 我可以想象……你對父母坦白自己是個魔術(shù)師,他們說:“我們得給他買臺電腦,或者別的什么……讓他去編程吧?!?/p>

Mat Ryer: 是的,讓他成為一個 Go 程序員,因為這里沒有魔法。我不知道一個家庭為什么會反對魔法,但……也許有一些隱藏的背景故事。

Jon Calhoun: 也許你父母是覺得:“他得搬出去住了。這行不掙錢。”

Mat Ryer: [笑]

Jon Calhoun: 我其實不知道魔術(shù)師掙多少錢,但我猜應(yīng)該很難入行……

Mat Ryer: 是的,我覺得很困難……嗯,我想不出笑話……真可惜,因為這里肯定有很多笑話等著讓我從空氣中抓出來。嗯,差不多夠了……我喜歡講完笑話后的尷尬沉默,那是我最喜歡的部分。

參考資料

[1]

Mat: https://github.com/matryer

[2]Jon: https://github.com/joncalhoun

[3]Jaana: https://github.com/rakyll

[4]#133 Reflection and meta programming: https://changelog.com/gotime/133

[5]Oto: https://github.com/pacedotdev/oto

[6]github.com/pacedotdev/oto: https://github.com/pacedotdev/oto

[7]SQLBoiler: https://github.com/volatiletech/sqlboiler

責(zé)任編輯:武曉燕 來源: 旅途散記
相關(guān)推薦

2023-07-10 08:36:21

工具pptword

2024-01-18 09:38:00

Java注解JDK5

2023-01-29 08:08:34

并發(fā)庫conc通用庫

2024-06-12 08:36:25

2022-12-26 07:48:04

敏捷項目

2024-03-05 10:09:16

restfulHTTPAPI

2023-03-07 07:50:15

Transactio事務(wù)代碼

2022-04-13 09:01:45

SASSCSS處理器

2022-09-26 08:49:11

Java架構(gòu)CPU

2022-12-08 10:49:43

2024-08-19 10:24:14

2022-07-11 09:00:37

依賴配置文件Mybati

2022-08-29 08:05:44

Go類型JSON

2023-06-05 08:36:04

SQL函數(shù)RANK()

2024-10-29 08:08:44

2022-03-05 23:29:18

LibuvwatchdogNode.js

2022-12-14 08:31:43

#error編譯命令

2023-02-15 08:41:56

多層維表性能寬表

2024-10-09 07:40:43

2024-03-04 07:41:18

SpringAOPOOP?
點贊
收藏

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