不是嚴謹的思考, 只是梳理一下感覺, 最近在動態類型靜態類型之間切換有點多, 對照思考.
個人經驗基本上是 js, ts 和 ClojureScript 上邊, 再有點 Nim 的使用經驗.
而後 Go 跟 Haskell 都只是簡單嘗試過, 沒有深刻進去.
這些個語言都是自動內存管理的, 因此內存往下深刻的我也不討論了.html
動態類型, 介紹數據結構時候說數組, 說字典, 而後就是基本的操做.
基本上只是有一個類型結構, 運行時才能判斷具體的類型, 但這個對思惟模型來講限制少.
好比考慮一個數組, 就是數組, 什麼均可以放進去, 數字, 字符串, 對象, 正則, 嵌套數組也行.
在實現層面, 我喜歡叫這個 uni-type, 由於內部就是一個結構包含不少信息來表示各類類型.
編程的時候, 考慮的就是有這麼個結構, 有這麼個 value, 至於 value 是什麼, 動態的, 能夠隨意放.程序員
靜態類型, 考慮就不少了, 特別是內存佈局方面的,
Nim 裏邊就是 object(相似 struct), seq, array, list, 多種的結構.
每一個結構會對應的到一個內存的佈局的方式. 細節具體怎麼佈局, 我瞭解不完全, 可是模糊地對應上.
特別是靜態類型編碼的時候不一樣結構之間性能的差異, 比較容易顯露出來.
我用錯告終構的時候, 數組的地方用鏈表, 訪問速度的差距立刻就出來了.
並且結構對類型限制明確, int 數組就不能有 string 了, 這個也是明顯的.編程
從業務編碼的角度來講, 用動態類型來模擬一些業務邏輯, 一般仍是比較輕易的.
而要用靜態類型, 常常須要有一些思考, 用怎麼樣的類型來表示, 受到什麼樣的限制.
定義告終構, 還有怎麼訪問怎麼遍歷的問題.
通常說好比 js 性能優化較全面了, 通常場景都不會去考慮不一樣寫法的性能差異了,
而靜態類型, 表示的方式, 訪問的方式, 都比較容易對性能形成影響.
靜態類型更貼近機器, 或者說用靜態類型, 就更加須要熟悉編碼器的各類行爲, 以便生成高性能代碼.json
Clojure 這邊還有個較爲特殊的例子, 就是 Vector 類型 HashMap 類型內部是用 B+ 樹表示的.
通常動態類型語言不會去強調這種內部的實現, 可是 Clojure 出於性能緣由高調宣傳了一下.
React 這邊, 不是被 immutablejs 搞得沸沸揚揚的, 以前你們也不太注意內部實現什麼樣子.
我用 Nim 時間長了一點發現這中抽象方式在靜態類型當中仍是蠻廣泛的, 數據結構嘛, 鏈表, B 樹...數組
並且有個不小的不適應的點, 在動態類型當中, 一個深度嵌套的數據結構, 直接打印就行了,
好比一個大的 JSON, 打印的時候能夠就遞歸地格式化掉. prettier 一下就能看.
可是靜態類型, Nim 當中的例子, 有時候數據就是不知道怎麼打印的,
由於複雜數據沒有一個 $
(表示 toString
) 函數, 那麼就沒法打印.
這個在 Haskell 當中也是, show
有時候經過 derive 繼承過來, 有時候就不行.
因爲這一點, 靜態類型就比較容易隱藏掉一個數據的內部實現了.
而動態類型, js Clojure 的數據, 通常就是直接遞歸打印出來.
js 用 class 的話我記得能從新定義掉, 不過 React 這邊也不樂意去用 class.
並且 js 程序員大多也習慣了 Console 當中層層展開直接查看數據的.
我以爲有封裝能力對於程序結構來講是更好的, 雖然便利性確實有折扣.性能優化
動態類型, 不一樣的程序經過字符串交換數據的時候, 簡單粗暴, JSON stringify/parse 就搞定了.
JSON 如今是很是通用的結構了. Clojure 那邊也是認爲這是動態語言巨大的優勢.
這就是程序基礎的結構, 跨程序跨語言都共通的結構, 這纔有普遍的通用性.
而 nil 在當中扮演的角色也比較重要, 由於外部來源的數據極可能就是缺失內容的, 大量的 nil.數據結構
靜態類型在數據接收傳遞上就存在限制了, 大量的 nil, 判斷起來就很麻煩.
protobuf 我沒用過, 調研的時候看說的, 二進制數據須要類型文件才能解析,
並且結構也是嚴格定義的, 不能錯. protobuf 還額外加上限制, 不能刪除字段.
從編碼器的角度看這邊是比較好理解的, 爲了方便生成代碼, 這些外來的數據都應該儘可能準確單一,
不單一不穩定的話, 就須要額外生成不少的代碼來處理特殊狀況, 就很麻煩.
Nim 的例子我試了, 但這個主要是靠 macro 在簡單場景能提供很方便的寫法,
實際用的話, Nim 處理一大堆的字段, 先用 JSON 獲取, 再讀取, 也很囉嗦.框架
代數類型, 算是另外一個方向吧. 按照我前面的思路, 它的表達能力偏向於動態類型,
可是相比於硬件的限制, 代數類型感受更可能是收到數學定義的限制,
好比 data A = B | C
這種或的邏輯, 硬件上表示會囉嗦很多,
而代數類型經過內置的語法, 不管建立, 操做, 仍是類型檢查, 都比單純靜態類型方便.
另外有名的例子就是 Maybe string
這樣的類型來替代 nil
了.
我沒什麼使用經驗, 很差判斷這個實用性.編程語言
動態類型, 默認也沒什麼校驗的, 數據不對, 執行的時候報錯, 就這樣了.
須要校驗的話都是動態地按照規則去校驗, 好比 Clojure 用的 spec, js schema 之類的.
動態有個好處是這中間的邏輯能夠用編程語言直接去表達出來, 很是靈活.
and or 這樣的邏輯操做隨便用, if switch 也能用, 其餘邏輯也是,
這就有了比較大的發揮的空間了, 封裝成不一樣的類庫.函數
靜態類型, 從生成代碼的角度, 這個代碼能運行就是已經通過校驗的,
或者應該說, 程序能運行, 就是說編碼的數據知足了一些編碼器要求的限制的.seq[int]
當中是 int
, 錯的話代碼編譯就會報錯. 除了業務, 通常也沒什麼須要類型校驗的.
但這個, 相對來講也就是編碼器要的那種程度, 對於業務來講是比較死板的.
另外有個比較花哨的, TypeScript, 實際運行容許 any, 就顯得很是特殊了.
我傾向於認爲 ts 就是個 type checker, 而不是當作一個 compiler.
ts 當中也借鑑了代數類型作了一些 union type, product type, 很是強大,
只是說跟前二者相比, 是一個很是奇特的思路了.
代數類型, Haskell 有個 quickcheck, 能經過類型自動生成隨機數據測試, 據說是很是強大的,
Clojure Spec 部分山寨了這樣的功能, 依賴的是運行時加上 macro 一些能力.
靜態類型這邊要這麼作, 就要反射機制了, 這個我不熟悉... 這整塊東西感受水就比較深了.
動態類型, 仍是前面 uni-type 的想法, 數據不少時認爲是一個類型, 沒得區分的.
具體運行的話, 就有個專門的字段好比 .kind
標記, 用來判斷類型,
而後針對不一樣類型要調用不一樣的方法的話, 也就能作針對性的操做了.
動態語言, 多態靠的對象繼承的方式來實現. 綁在類上邊有個函數方法.
固然, 這個在 React Clojure 生態裏邊, 這種就比較少了.
特別是數據還以 JSON 形式處處分發, 更多的仍是用 .kind
標記, 再調用不一樣函數.
靜態類型這邊, 當我使用的 Nim 的時候, 就發現這裏多態就很天然有個方案, 由於有類型,
proc f(a: int): bool proc f(a: string): bool
實際調用 a.f()
(Nim 裏邊這是 f(a)
的縮寫)的時候, 根據 a
的類型就有對應的方法.
我聯想起來 Haskell 定義 class instance 的時候相似也是這樣的,
OOP 不是我熟悉的領域我也很差說了, 可是這個場景有類型, 解決方案就很天然.
而動態類型, 我定義一個 join
, 就要用判斷不一樣類型再針對處理了, 也不方便擴展.
我以爲這一點對咱們理解數據比較有影響, 由於數據每每是要被操做的.
我是把數據認爲是樹狀的一大個, 而後須要函數動態地去判斷, 而後再操做呢?
仍是說認爲數據一塊一塊相互拼湊, 可是各自有清晰的類型, 而後有各自的方法?
仍是說跟原始的面向對象那樣, 就是一個對象, 接受消息, 發出消息?
我很差說這些觀念互斥, 可是好比說你設計一個簡單的框架, 別人按照框架來快速完成功能,
那麼在這個框架當中, 別人怎麼去理解數據這個事情, 固然還有狀態, 這中間的關係和功能?
由於我是 ClojureScript 用得比較習慣, 我就更認爲是樹狀的一大串.
上邊梳理的基本仍是零碎的想法, 最終仍是會回到抽象能力這個事情上來.
固然開發當中有不一樣的場景, 不一樣的方案並非說某個就是不對的, 它最少也是有一個適合的場景.
可是這種, 對於思考業務的差異仍是挺大的, 反應在不一樣的編程語言當中.
好比 Clojure 當中, 動態的, 並且強調不可變數據, 就會致使全局存儲樹形的數據結構,
我看待狀態的時候, 在這類場景當中就是一棵多是很深的樹, 而後判斷.
由於是動態的, 也是樹, 我就會很是隨意地容許一些配置化的數據隨處出現,
我也不那麼在意這個結構是否是個單元, 須要的時候, 我就寫個函數, 動態處理一下就行了.
而 Nim 當中, 定義類型, 枚舉, 結構體, 這些概念就很基礎,
我定義一個函數, 也要想要函數的操做對象首先是誰, 哪些可變哪些不可變.
這個跟面向對象那種思路也相近一些, 他能被怎樣操做有哪些方法, 都會想想.
而後若是有深一點的數據, 很明顯就是這些小的結構的組合了.
我用動態類型的話, 比較容易會淡化掉這種結構性的考慮, 由於建立新的結構成本很小,
不用說兩個類似的數據, 多一個屬性少一個屬性, 考慮是否是去複用,
原本你們就是 uni-type 了, 你們都是同樣的, 只是內部包含的信息動態有差別.
這個隨意性, 對於性能來講是很差的, 可是編碼當中便利是存在的.
靜態類型, 固然, 性能好.
此外, 還有個狀況, 就是有類型推斷參與的場景,
因爲類型推斷依賴一些假設, 這中間的情形就隨着變複雜. 就感受挺混搭的.
好比 TypeScript 依據開頭建立的字段和元素簡歷類型了, 可是後邊編碼可能又被推翻掉.
再展開點想, 若是隻是爲了完成業務, 甚至都不限於使用某個編程語言,
那麼咱們描述的業務用的 DSL 當中, 爲了也是專門定製的數據,
這個時候數據類型啊, 操做方法啊, 這裏的數據該往怎麼樣的形態上演變?
我沒有個清晰的結論. 本能得我會但願業務上我能動態類型隨便上,固然我也知道類型推斷提供的類型支持, 對於避免程序漏洞很是強力,並且一些基礎的數據結構, 能抽象出來封裝好稱爲一個類型, 好處也不少.大概後邊要對 Haskell 那樣的體系仍是要增長一些瞭解, 反過來幫助理解動態類型.