Pofat 的話
上一期泛型話題源自於我在 Swift 論壇中看到的一篇回文,解開了許多我對於 Swift 為什麼不那樣這樣的疑惑,深切地認為這種好文不分享實在不行,所以才有了前期的思想前導文(?)
而這期則是這篇回文的內容分享,從真實的問題回頭看語言設計的緣由。
什麼是 any 和 some ?談 Swift 的泛型思想 (下)
上一期提到 some
是某個由 compiler 反向推導得到的型別, any
則是個經理容器,那這兩個概念對於 Swift 提倡的 Protocol Oriented Programming 有沒有幫助呢?
讓我們先回顧最常見的 protocol 使用場合 - 測試,我們常把容器用於需在測試時抽換的對象上,如此一來可以輕易切換 app 實際執行和測試時的邏輯。
但是當你需要 return 無法自己 init 的物件時頭就大了,比如 CBPeripheral。一般做法會用 Peripheral protocol 做為物件容器,在上層再宣告一個 manager protocol 做為 manager 的容器,但正當你要遵循 manager protocol 時會發現 Swift 不支援 return 的型別轉換 (covariant),在這裡是 [Peripheral] 和 [CBPeripheral] 不能互相轉換,便遵循不了。嘗試下面的程式碼會發現 protocol 並沒有被滿足,也就無法達成抽換邏輯的目的:
Swift core team 的 Ben Cohen 在論壇上回覆了這個問題,精彩地講述了 Swift 對於這方面的設計理念和背後原因。首先,利用 protocol 裡的泛型 (associated type)可以解決問題:
那為什麼不像其它語言採用可自動轉換型別的作法,讓 Peripheral
在直接代換成 CBPeripheral
呢?像是在 ObjC 裡可以把子型別餵給一個參數指定為父型別的 method:
基本上只要是以指標(pointer)為基礎的語言,這種轉換就是免成本的舉手之勞,永遠可以把 sub class 餵給 parent class ,因為裡面的結構相同,都是指標,而且指向的內容裡會包含 parent calss。
但對於 Swfit 這樣有值語義的語言來說,此種轉換不可行、或是會帶來很高的風險,因為值類型(如 struct MockPeripheral: Peripheral
)是會直接在 array 中展開的,概念如下:
而引用類型的 array 裡放的則是一個個指向真實物件的指標,結構上有截然的不同。如此一來應該不難理解強調型別安全的 Swift 不往這個方向發展。
So instead of techniques like type erasure and covariance, Swift solves this with generics – with a protocol with an associated type.
而 some
和 any
都在減少 Protocol with associated type 的使用困難以及擴大應用場景,兼顧了型別明確的正義以及彈性。
最後分享個人區分 some
和 any
使用場景的心得:
盡量優先使用
some
,因為它是真實型別,限制是在同一個 scope 裡some
只能代表某「一」種型別。當同一個 scope 內需要「多」種型別時,你就會需要
any
這個容器,比如可以同時存放 String 與 Int 等滿足 Equatable 的 array:let equatables: [any Equatable]
Swift 新聞
📝 SE-0362 Piecemeal adoption of future language improvements (Reviewing)
凡是會影響現有 source code 的 Swift Evolution 通常會集合到 Swift 的大版本再一起更新(同步 WWDC), 想先嘗為快的可以透過給 compiler 不同 flag 來開啟。
這個提案在於統一介面以讓大家更容易地當白老鼠先行使用,其中分成「啟用」與「使用」兩種情境:
此類的功能將被賦予「名稱」 (feature identifier),比如最近啟用檢查
any
的功能叫ExistentialAny
透過
-enable-future-feature $featureName
這個 compiler flag 來啟用你想要的功能SPM 也同樣能定義它需要的「未來功能」
程式碼裡則能夠使用關鍵字來偵測某功能是否已啟用,以處理不支援時的邏輯,比如預計 Swift 6 才支援的 Regex literal (
/..../
)
(註:偵測啟用的關鍵字目前有 hasFeature(X)
和 $(X)
兩個候選人)
🙅♂️ 不要把別人的物件遵循別人的 protocol
如果你用的 framework 某個版本把該物件遵循了 protocol,就會和你自己寫的打架,而且你只能處於被打的一方,若是系統 framework 則更崩潰。
遇到這種需求時,請記得用一個 wrapper 把物件包起來,只讓 wrapper 遵循 protocol 就好,再搭配 @dynamicMemberLookup
,就可以用 wrapper 如用本物了!
附上結合上述的完整程式碼:
🧰 macOS VM 的管理 App
正當有人呼喊 macOS 的模擬器以測 SwiftUI 寫的 mac app 時,藍波哥利用今年的新 API 推出了 SwiftUI 做的 VM 管理 App。
🧰 將傳統 Regex 字串轉換成 Swift RegexBuilder
有位推友做出在網頁上輸入 regex 字串便能得到對應 RegexBuilder 展開示的小工具,試玩後發現除了方便外,也幫助我稍微搞懂從沒懂過的 regex 字串內容。
雖然說我試了防守率高達 99.99% 的終極 email 辨別字串之後,好像更看不懂了內🤢
📢 Sever-Side Swift State Of The Union
Time Condon (現任 Swift Server Workgroup 成員)在今年五月的 SF Swift 講了 Swift on Server-Side 的現況、以及未來發展,其影片在這週公開給大眾觀看。
個人摘要幾個現況和未來亮點:
已經在 4.50.0 支援 async/await,今日起即可使用
已開源 Swift Distributed Tracking,用於 debug 分散式服務中出錯的環節
Vapor 5 的目標是全面用 async 重寫
會與 SwiftNIO 3 與 Swift 6 發展同步
大家最關心的應該還是,「Swift 到底能不能用於 Server 開發?」,Tim 本人的老王賣瓜回答是:
目前已知 Benz,Amazon,Spotify 和 ING 銀行等公司已用 Swift 打造部分服務(相信也是入坑最佳的起手式),Swift 本身在語言面的效能和記憶體用量都算不錯,現在有了 async/await 的加持,看起來吸引力是增加不少。
推薦觀望中的人可以看看了解現況是否滿足你的需求來評估入坑與否。
Pofat 選推
相容性話題永不退流行
Swift 官方宣傳已經推出一段時間的功能 - Property Wrappers,但最近受到 Extreme Lat Swift Tips 荼毒,一度以為是它,沒想到是本帳啊...
這真是我看過 ShazamKit 最有創意的應用了,利用 ShazamKit 判斷曲目進行的位置,到翻頁點時自動換下一頁樂譜,太聰明了吧!
Standup meeting 永遠的報告內容: