Pofat 的話
波報在 13 和 weak self 的宣傳助力下一口氣,第一周就累積了遠超預期的訂閱數,謝謝大家的支持,同時也請繼續支持剛升級為 13 宇宙的「13 的 Apple 開發者電子報」,詳情見此。
雖然WWDC22 僅剩一周的時間,Swift 的程式碼仍然是動得佷厲害,關於泛型和 Regex 都一直有新的東西在審查和實作中,尤其是幾個泛型提案一解 Swift 開發者多年來的心頭之痛,雖然不確定會在哪版發佈,但未來是可以期待的,這期就讓我們一起來看看這些改動是什麼。此外,若想結伴參與 WWDC 卻找不到伴,或者想一同來場 keynote watch party,歡迎加入 weak self 的 Discord。
Swift 新聞
泛型之旅的階段性終點不遠矣(吧)
2016 年時 Douglas Gregor 寫的 Generic Manifesto 為現在的 Protocol 與泛型功能之基石,事隔三年 Joe Groff 再提了另外一篇講述「怎麼讓泛型更好用」(Improving the UI of generics),這一切問題的根源都在於「 Swift 允許你用型別的寫法寫 Protocol (語法上的混淆),但它其實是一個 existential 容器(語意上的不清),而且有 associatedtype 時你又不能這樣用(讓人又搞不懂又恨的限制)」,其中提到的三大項改進方針如今都已經快要看到終點線了只是終點線有時會移動,目前已完工的部分完整了大多基礎建設,如 SwiftUI 隨處可見的 some View
,讓 compiler 自行推敲 Protocol 的真實型別;以及 SE-309 Unlock existentials for all protocols 裡解放所有 Protocol ,使其皆有建立 existential container 的權利,一迷因以蔽之就是:
剩下的部分都是語法相關的改善,也就是更多的新語法,大家可以先不用太擔心對於自己專案的衝擊,因為那些多是「本來不能這麼寫但大家都很想這麼寫」的改進,幾個相關的有:
[✅ In Swift 5.7] SE-0346 Lightweight same-type requirements for primary associated types
這個 SE 給予我們在定義 Protocol 時可以挑一個以上的 associated type 出來放在 < >
裡,稱作 primary associatedtype(s), 在使用時我們能直接指定它的型別是什麼,宣告起來和我們在一般 struct / class 宣告泛型一致,也符合心智模型 - 即 associated type 就是 Protocol 的泛型。 Standard Library 裡面常見的 AnyCollection 等用來抺除類型的系列,也是因為這個先天不足。用看的會更明白:
// 原本宣告:
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Element == Iterator.Element
}
// 改成可以這樣宣告,Element 做為 primary associatedtype:
protocol Sequence<Element> {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Element == Iterator.Element
}
// 原本用法:
func merge<S: Sequence>(_ lhs: S, _ rhs: S) -> S where S.Element == String
// 改成可以這樣使用:
func merge<S: Sequence<String>>(_ lhs: S, _ rhs: S) -> S
// 以及新增寫法:
func readLines(from filePath: String) -> some Sequence<String> { ... }
[👀 Reviewing] SE-0358 Primary Associated Types in the Standard Library
看完前述 SE-0346 的功能,是不是突然一驚那 Standard Library 裡不就一堆 Protocol 要改?你想的沒錯,SE-0358 正是要把一大堆的定義改寫,並為其定義 primary associatedtype(s),比如 protocol RawRepresentable
會變成 RawRepresentable<RawValue>
。除此之外,裡面還提供了對於使用這新語法 API 的設計通則,基本上就是讓呼叫方清楚明白做人做事的道理,最多一個 primary associatedtype,建議 framework 開發者可以一讀。
個人對這個 SE 相當興奮,寫電子報的同天正好在 weak self Discord 看直播寫程式時聊到總程式碼量少對於開發時間縮短是有幫助的,因為基本成本就是打字時間,只是簡短和清楚的平衡相當不容易。
[🉑 Accepted] SE-0352 Implicitly Opened Existentials
先一段 code 來了解這個 SE 之緣由:
protocol Animal {
func sound() -> String
}
struct Dog: Animal {
func sound() -> String {
return "roof"
}
}
let dog: any Animal = Dog()
func hear<T: Animal>(animal: T) {
print(animal.sound())
}
hear(animal: dog) // error: Protocol 'Animal' as a type cannot conform to the protocol itself
以上的程式碼會得到錯誤: Protocol 'Animal' as a type cannot conform to the protocol itself,那是因為 dog
是一個 existential container ,是個容器而不遵循 Animal
,唯有 Error 是唯一特例可以這樣搞。當 SE-309 允許大家亂寫各種 any
後,SE-352 就是來除掉這個問題。
它的做法其實就是看到是 any
的容器後打開查裡面的真正類型,再把泛型參數 T
跟真正的類型綁定,這件事當然只能在 runtime 完成,但至少還是面對一個靜態的型別,相對安全。其實這不是新做法,現在呼叫 Protocol 的 extension 時,也會去查 Self
是什麼。
[🉑 Accepted] SE-0353 Constrained Existential Types
這個 SE 一償了 Swift 開發者多年以來的夙願啊!!
我們終於可以「宣告一個 Array ,它的Element 是帶有 associatedtype 的 Protocol」,簡而言之,你能宣告這些東西了:
let values: [any RawRepresentable<Int>]
let stringCollection: any Collection<String>
其它
[👀 Reviewing] SE-0356 Swift Snippets
滿有趣的 DocC 新功能,可以直接把模版裡寫的程式碼輸出成單一檔案的 snippet ,而且可以透過 HIDE
和 Show
(吐個嘈,大小寫統一一下好嗎)來控制說明文件上的程式碼可以和最終輸出到 snippet 裡的不一樣,讓你的使用者可以直接複製貼上,非常貼心🫰。
Pofat 選推
小知識:在 Swift docstrings 裡雙反引號包起來的 method 在文件畫會變成連結,非常酷炫。
WWDC20 的 sample code 還沒給,都 2022 啦!
🎙weak self podcast 97: 報社開起來
下一步會是拍片嗎?(先不要
拋開歷史包袱不看,你覺得接下來 Swift 在 protocol 與 generics 給使用者的心智模型會變簡單嗎?會變得更能「無腦用」?